Removed a superfluous debug_print() call.
[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         debug_print("main text: %zd bytes encoded as %s in %d\n",
5676                 strlen(buf), out_codeset, encoding);
5677
5678         /* check for line length limit */
5679         if (action == COMPOSE_WRITE_FOR_SEND &&
5680             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5681             check_line_length(buf, 1000, &line) < 0) {
5682                 AlertValue aval;
5683
5684                 msg = g_strdup_printf
5685                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5686                            "The contents of the message might be broken on the way to the delivery.\n"
5687                            "\n"
5688                            "Send it anyway?"), line + 1);
5689                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5690                 g_free(msg);
5691                 if (aval != G_ALERTALTERNATE) {
5692                         g_free(buf);
5693                         return -1;
5694                 }
5695         }
5696         
5697         if (encoding != ENC_UNKNOWN)
5698                 procmime_encode_content(mimetext, encoding);
5699
5700         /* append attachment parts */
5701         if (compose_use_attach(compose) && attach_parts) {
5702                 MimeInfo *mimempart;
5703                 gchar *boundary = NULL;
5704                 mimempart = procmime_mimeinfo_new();
5705                 mimempart->content = MIMECONTENT_EMPTY;
5706                 mimempart->type = MIMETYPE_MULTIPART;
5707                 mimempart->subtype = g_strdup("mixed");
5708
5709                 do {
5710                         g_free(boundary);
5711                         boundary = generate_mime_boundary(NULL);
5712                 } while (strstr(buf, boundary) != NULL);
5713
5714                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5715                                     boundary);
5716
5717                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5718
5719                 g_node_append(mimempart->node, mimetext->node);
5720                 g_node_append(mimemsg->node, mimempart->node);
5721
5722                 if (compose_add_attachments(compose, mimempart) < 0)
5723                         return -1;
5724         } else
5725                 g_node_append(mimemsg->node, mimetext->node);
5726
5727         g_free(buf);
5728
5729         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5730                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5731                 /* extract name and address */
5732                 if (strstr(spec, " <") && strstr(spec, ">")) {
5733                         from_addr = g_strdup(strrchr(spec, '<')+1);
5734                         *(strrchr(from_addr, '>')) = '\0';
5735                         from_name = g_strdup(spec);
5736                         *(strrchr(from_name, '<')) = '\0';
5737                 } else {
5738                         from_name = NULL;
5739                         from_addr = NULL;
5740                 }
5741                 g_free(spec);
5742         }
5743         /* sign message if sending */
5744         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5745             privacy_system_can_sign(compose->privacy_system))
5746                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5747                         compose->account, from_addr)) {
5748                         g_free(from_name);
5749                         g_free(from_addr);
5750                         return -2;
5751         }
5752         g_free(from_name);
5753         g_free(from_addr);
5754
5755         if (compose->use_encryption) {
5756                 if (compose->encdata != NULL &&
5757                                 strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
5758
5759                         /* First, write an unencrypted copy and save it to outbox, if
5760                          * user wants that. */
5761                         if (compose->account->save_encrypted_as_clear_text) {
5762                                 debug_print("saving sent message unencrypted...\n");
5763                                 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
5764                                 if (tmpfp) {
5765                                         fclose(tmpfp);
5766
5767                                         /* fp now points to a file with headers written,
5768                                          * let's make a copy. */
5769                                         rewind(fp);
5770                                         content = file_read_stream_to_str(fp);
5771
5772                                         str_write_to_file(content, tmp_enc_file);
5773                                         g_free(content);
5774
5775                                         /* Now write the unencrypted body. */
5776                                         if ((tmpfp = g_fopen(tmp_enc_file, "a")) != NULL) {
5777                                                 procmime_write_mimeinfo(mimemsg, tmpfp);
5778                                                 fclose(tmpfp);
5779
5780                                                 outbox = folder_find_item_from_identifier(compose_get_save_to(compose));
5781                                                 if (!outbox)
5782                                                         outbox = folder_get_default_outbox();
5783
5784                                                 procmsg_save_to_outbox(outbox, tmp_enc_file, TRUE);
5785                                                 claws_unlink(tmp_enc_file);
5786                                         } else {
5787                                                 g_warning("Can't open file '%s'", tmp_enc_file);
5788                                         }
5789                                 } else {
5790                                         g_warning("couldn't get tempfile");
5791                                 }
5792                         }
5793                         if (!privacy_encrypt(compose->privacy_system, mimemsg, compose->encdata)) {
5794                                 debug_print("Couldn't encrypt mime structure: %s.\n",
5795                                                 privacy_get_error());
5796                                 alertpanel_error(_("Couldn't encrypt the email: %s"),
5797                                                 privacy_get_error());
5798                         }
5799                 }
5800         }
5801
5802         procmime_write_mimeinfo(mimemsg, fp);
5803         
5804         procmime_mimeinfo_free_all(&mimemsg);
5805
5806         return 0;
5807 }
5808
5809 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5810 {
5811         GtkTextBuffer *buffer;
5812         GtkTextIter start, end;
5813         FILE *fp;
5814         size_t len;
5815         gchar *chars, *tmp;
5816
5817         if ((fp = g_fopen(file, "wb")) == NULL) {
5818                 FILE_OP_ERROR(file, "fopen");
5819                 return -1;
5820         }
5821
5822         /* chmod for security */
5823         if (change_file_mode_rw(fp, file) < 0) {
5824                 FILE_OP_ERROR(file, "chmod");
5825                 g_warning("can't change file mode");
5826         }
5827
5828         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5829         gtk_text_buffer_get_start_iter(buffer, &start);
5830         gtk_text_buffer_get_end_iter(buffer, &end);
5831         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5832
5833         chars = conv_codeset_strdup
5834                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5835
5836         g_free(tmp);
5837         if (!chars) {
5838                 fclose(fp);
5839                 claws_unlink(file);
5840                 return -1;
5841         }
5842         /* write body */
5843         len = strlen(chars);
5844         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5845                 FILE_OP_ERROR(file, "fwrite");
5846                 g_free(chars);
5847                 fclose(fp);
5848                 claws_unlink(file);
5849                 return -1;
5850         }
5851
5852         g_free(chars);
5853
5854         if (fclose(fp) == EOF) {
5855                 FILE_OP_ERROR(file, "fclose");
5856                 claws_unlink(file);
5857                 return -1;
5858         }
5859         return 0;
5860 }
5861
5862 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5863 {
5864         FolderItem *item;
5865         MsgInfo *msginfo = compose->targetinfo;
5866
5867         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5868         if (!msginfo) return -1;
5869
5870         if (!force && MSG_IS_LOCKED(msginfo->flags))
5871                 return 0;
5872
5873         item = msginfo->folder;
5874         cm_return_val_if_fail(item != NULL, -1);
5875
5876         if (procmsg_msg_exist(msginfo) &&
5877             (folder_has_parent_of_type(item, F_QUEUE) ||
5878              folder_has_parent_of_type(item, F_DRAFT) 
5879              || msginfo == compose->autosaved_draft)) {
5880                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5881                         g_warning("can't remove the old message");
5882                         return -1;
5883                 } else {
5884                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5885                 }
5886         }
5887
5888         return 0;
5889 }
5890
5891 static void compose_remove_draft(Compose *compose)
5892 {
5893         FolderItem *drafts;
5894         MsgInfo *msginfo = compose->targetinfo;
5895         drafts = account_get_special_folder(compose->account, F_DRAFT);
5896
5897         if (procmsg_msg_exist(msginfo)) {
5898                 folder_item_remove_msg(drafts, msginfo->msgnum);
5899         }
5900
5901 }
5902
5903 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5904                    gboolean remove_reedit_target)
5905 {
5906         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5907 }
5908
5909 static gboolean compose_warn_encryption(Compose *compose)
5910 {
5911         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5912         AlertValue val = G_ALERTALTERNATE;
5913         
5914         if (warning == NULL)
5915                 return TRUE;
5916
5917         val = alertpanel_full(_("Encryption warning"), warning,
5918                   GTK_STOCK_CANCEL, g_strconcat("+", _("C_ontinue"), NULL), NULL,
5919                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5920         if (val & G_ALERTDISABLE) {
5921                 val &= ~G_ALERTDISABLE;
5922                 if (val == G_ALERTALTERNATE)
5923                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5924                                 TRUE);
5925         }
5926
5927         if (val == G_ALERTALTERNATE) {
5928                 return TRUE;
5929         } else {
5930                 return FALSE;
5931         } 
5932 }
5933
5934 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5935                               gchar **msgpath, gboolean check_subject,
5936                               gboolean remove_reedit_target)
5937 {
5938         FolderItem *queue;
5939         gchar *tmp;
5940         FILE *fp;
5941         GSList *cur;
5942         gint num;
5943         PrefsAccount *mailac = NULL, *newsac = NULL;
5944         gboolean err = FALSE;
5945
5946         debug_print("queueing message...\n");
5947         cm_return_val_if_fail(compose->account != NULL, -1);
5948
5949         if (compose_check_entries(compose, check_subject) == FALSE) {
5950                 if (compose->batch) {
5951                         gtk_widget_show_all(compose->window);
5952                 }
5953                 return -1;
5954         }
5955
5956         if (!compose->to_list && !compose->newsgroup_list) {
5957                 g_warning("can't get recipient list.");
5958                 return -1;
5959         }
5960
5961         if (compose->to_list) {
5962                 if (compose->account->protocol != A_NNTP)
5963                         mailac = compose->account;
5964                 else if (cur_account && cur_account->protocol != A_NNTP)
5965                         mailac = cur_account;
5966                 else if (!(mailac = compose_current_mail_account())) {
5967                         alertpanel_error(_("No account for sending mails available!"));
5968                         return -1;
5969                 }
5970         }
5971
5972         if (compose->newsgroup_list) {
5973                 if (compose->account->protocol == A_NNTP)
5974                         newsac = compose->account;
5975                 else {
5976                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5977                         return -1;
5978                 }                       
5979         }
5980
5981         /* write queue header */
5982         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5983                               G_DIR_SEPARATOR, compose, (guint) rand());
5984         debug_print("queuing to %s\n", tmp);
5985         if ((fp = g_fopen(tmp, "w+b")) == NULL) {
5986                 FILE_OP_ERROR(tmp, "fopen");
5987                 g_free(tmp);
5988                 return -2;
5989         }
5990
5991         if (change_file_mode_rw(fp, tmp) < 0) {
5992                 FILE_OP_ERROR(tmp, "chmod");
5993                 g_warning("can't change file mode");
5994         }
5995
5996         /* queueing variables */
5997         err |= (fprintf(fp, "AF:\n") < 0);
5998         err |= (fprintf(fp, "NF:0\n") < 0);
5999         err |= (fprintf(fp, "PS:10\n") < 0);
6000         err |= (fprintf(fp, "SRH:1\n") < 0);
6001         err |= (fprintf(fp, "SFN:\n") < 0);
6002         err |= (fprintf(fp, "DSR:\n") < 0);
6003         if (compose->msgid)
6004                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
6005         else
6006                 err |= (fprintf(fp, "MID:\n") < 0);
6007         err |= (fprintf(fp, "CFG:\n") < 0);
6008         err |= (fprintf(fp, "PT:0\n") < 0);
6009         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
6010         err |= (fprintf(fp, "RQ:\n") < 0);
6011         if (mailac)
6012                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
6013         else
6014                 err |= (fprintf(fp, "SSV:\n") < 0);
6015         if (newsac)
6016                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
6017         else
6018                 err |= (fprintf(fp, "NSV:\n") < 0);
6019         err |= (fprintf(fp, "SSH:\n") < 0);
6020         /* write recepient list */
6021         if (compose->to_list) {
6022                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
6023                 for (cur = compose->to_list->next; cur != NULL;
6024                      cur = cur->next)
6025                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
6026                 err |= (fprintf(fp, "\n") < 0);
6027         }
6028         /* write newsgroup list */
6029         if (compose->newsgroup_list) {
6030                 err |= (fprintf(fp, "NG:") < 0);
6031                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
6032                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
6033                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
6034                 err |= (fprintf(fp, "\n") < 0);
6035         }
6036         /* Sylpheed account IDs */
6037         if (mailac)
6038                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
6039         if (newsac)
6040                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
6041
6042         
6043         if (compose->privacy_system != NULL) {
6044                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
6045                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
6046                 if (compose->use_encryption) {
6047                         if (!compose_warn_encryption(compose)) {
6048                                 fclose(fp);
6049                                 claws_unlink(tmp);
6050                                 g_free(tmp);
6051                                 return -6;
6052                         }
6053                         if (mailac && mailac->encrypt_to_self) {
6054                                 GSList *tmp_list = g_slist_copy(compose->to_list);
6055                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
6056                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
6057                                 g_slist_free(tmp_list);
6058                         } else {
6059                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
6060                         }
6061                         if (compose->encdata != NULL) {
6062                                 if (strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
6063                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6064                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
6065                                                 compose->encdata) < 0);
6066                                 } /* else we finally dont want to encrypt */
6067                         } else {
6068                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6069                                 /* and if encdata was null, it means there's been a problem in 
6070                                  * key selection */
6071                                 if (err == TRUE)
6072                                         g_warning("failed to write queue message");
6073                                 fclose(fp);
6074                                 claws_unlink(tmp);
6075                                 g_free(tmp);
6076                                 return -5;
6077                         }
6078                 }
6079         }
6080
6081         /* Save copy folder */
6082         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6083                 gchar *savefolderid;
6084                 
6085                 savefolderid = compose_get_save_to(compose);
6086                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
6087                 g_free(savefolderid);
6088         }
6089         /* Save copy folder */
6090         if (compose->return_receipt) {
6091                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
6092         }
6093         /* Message-ID of message replying to */
6094         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
6095                 gchar *folderid = NULL;
6096
6097                 if (compose->replyinfo->folder)
6098                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
6099                 if (folderid == NULL)
6100                         folderid = g_strdup("NULL");
6101
6102                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
6103                 g_free(folderid);
6104         }
6105         /* Message-ID of message forwarding to */
6106         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
6107                 gchar *folderid = NULL;
6108                 
6109                 if (compose->fwdinfo->folder)
6110                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
6111                 if (folderid == NULL)
6112                         folderid = g_strdup("NULL");
6113
6114                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
6115                 g_free(folderid);
6116         }
6117
6118         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
6119         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
6120
6121         /* end of headers */
6122         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
6123
6124         if (compose->redirect_filename != NULL) {
6125                 if (compose_redirect_write_to_file(compose, fp) < 0) {
6126                         fclose(fp);
6127                         claws_unlink(tmp);
6128                         g_free(tmp);
6129                         return -2;
6130                 }
6131         } else {
6132                 gint result = 0;
6133                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
6134                         fclose(fp);
6135                         claws_unlink(tmp);
6136                         g_free(tmp);
6137                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6138                 }
6139         }
6140         if (err == TRUE) {
6141                 g_warning("failed to write queue message");
6142                 fclose(fp);
6143                 claws_unlink(tmp);
6144                 g_free(tmp);
6145                 return -2;
6146         }
6147         if (fclose(fp) == EOF) {
6148                 FILE_OP_ERROR(tmp, "fclose");
6149                 claws_unlink(tmp);
6150                 g_free(tmp);
6151                 return -2;
6152         }
6153
6154         if (item && *item) {
6155                 queue = *item;
6156         } else {
6157                 queue = account_get_special_folder(compose->account, F_QUEUE);
6158         }
6159         if (!queue) {
6160                 g_warning("can't find queue folder");
6161                 claws_unlink(tmp);
6162                 g_free(tmp);
6163                 return -1;
6164         }
6165         folder_item_scan(queue);
6166         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6167                 g_warning("can't queue the message");
6168                 claws_unlink(tmp);
6169                 g_free(tmp);
6170                 return -1;
6171         }
6172         
6173         if (msgpath == NULL) {
6174                 claws_unlink(tmp);
6175                 g_free(tmp);
6176         } else
6177                 *msgpath = tmp;
6178
6179         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6180                 compose_remove_reedit_target(compose, FALSE);
6181         }
6182
6183         if ((msgnum != NULL) && (item != NULL)) {
6184                 *msgnum = num;
6185                 *item = queue;
6186         }
6187
6188         return 0;
6189 }
6190
6191 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6192 {
6193         AttachInfo *ainfo;
6194         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6195         MimeInfo *mimepart;
6196         GStatBuf statbuf;
6197         gchar *type, *subtype;
6198         GtkTreeModel *model;
6199         GtkTreeIter iter;
6200
6201         model = gtk_tree_view_get_model(tree_view);
6202         
6203         if (!gtk_tree_model_get_iter_first(model, &iter))
6204                 return 0;
6205         do {
6206                 gtk_tree_model_get(model, &iter,
6207                                    COL_DATA, &ainfo,
6208                                    -1);
6209                 
6210                 if (!is_file_exist(ainfo->file)) {
6211                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6212                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6213                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6214                         g_free(msg);
6215                         if (val == G_ALERTDEFAULT) {
6216                                 return -1;
6217                         }
6218                         continue;
6219                 }
6220                 if (g_stat(ainfo->file, &statbuf) < 0)
6221                         return -1;
6222
6223                 mimepart = procmime_mimeinfo_new();
6224                 mimepart->content = MIMECONTENT_FILE;
6225                 mimepart->data.filename = g_strdup(ainfo->file);
6226                 mimepart->tmp = FALSE; /* or we destroy our attachment */
6227                 mimepart->offset = 0;
6228                 mimepart->length = statbuf.st_size;
6229
6230                 type = g_strdup(ainfo->content_type);
6231
6232                 if (!strchr(type, '/')) {
6233                         g_free(type);
6234                         type = g_strdup("application/octet-stream");
6235                 }
6236
6237                 subtype = strchr(type, '/') + 1;
6238                 *(subtype - 1) = '\0';
6239                 mimepart->type = procmime_get_media_type(type);
6240                 mimepart->subtype = g_strdup(subtype);
6241                 g_free(type);
6242
6243                 if (mimepart->type == MIMETYPE_MESSAGE && 
6244                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6245                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
6246                 } else if (mimepart->type == MIMETYPE_TEXT) {
6247                         if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6248                                 /* Text parts with no name come from multipart/alternative
6249                                 * forwards. Make sure the recipient won't look at the 
6250                                 * original HTML part by mistake. */
6251                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6252                                 ainfo->name = g_strdup_printf(_("Original %s part"),
6253                                                                 mimepart->subtype);
6254                         }
6255                         if (ainfo->charset)
6256                                 g_hash_table_insert(mimepart->typeparameters,
6257                                                     g_strdup("charset"), g_strdup(ainfo->charset));
6258                 }
6259                 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6260                         if (mimepart->type == MIMETYPE_APPLICATION && 
6261                            !strcmp2(mimepart->subtype, "octet-stream"))
6262                                 g_hash_table_insert(mimepart->typeparameters,
6263                                                 g_strdup("name"), g_strdup(ainfo->name));
6264                         g_hash_table_insert(mimepart->dispositionparameters,
6265                                         g_strdup("filename"), g_strdup(ainfo->name));
6266                         mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6267                 }
6268
6269                 if (mimepart->type == MIMETYPE_MESSAGE
6270                     || mimepart->type == MIMETYPE_MULTIPART)
6271                         ainfo->encoding = ENC_BINARY;
6272                 else if (compose->use_signing) {
6273                         if (ainfo->encoding == ENC_7BIT)
6274                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6275                         else if (ainfo->encoding == ENC_8BIT)
6276                                 ainfo->encoding = ENC_BASE64;
6277                 }
6278
6279                 
6280                 
6281                 procmime_encode_content(mimepart, ainfo->encoding);
6282
6283                 g_node_append(parent->node, mimepart->node);
6284         } while (gtk_tree_model_iter_next(model, &iter));
6285         
6286         return 0;
6287 }
6288
6289 static gchar *compose_quote_list_of_addresses(gchar *str)
6290 {
6291         GSList *list = NULL, *item = NULL;
6292         gchar *qname = NULL, *faddr = NULL, *result = NULL;
6293
6294         list = address_list_append_with_comments(list, str);
6295         for (item = list; item != NULL; item = item->next) {
6296                 gchar *spec = item->data;
6297                 gchar *endofname = strstr(spec, " <");
6298                 if (endofname != NULL) {
6299                         gchar * qqname;
6300                         *endofname = '\0';
6301                         QUOTE_IF_REQUIRED_NORMAL(qname, spec, return NULL);
6302                         qqname = escape_internal_quotes(qname, '"');
6303                         *endofname = ' ';
6304                         if (*qname != *spec || qqname != qname) { /* has been quoted, compute new */
6305                                 gchar *addr = g_strdup(endofname);
6306                                 gchar *name = (qqname != qname)? qqname: g_strdup(qname);
6307                                 faddr = g_strconcat(name, addr, NULL);
6308                                 g_free(name);
6309                                 g_free(addr);
6310                                 debug_print("new auto-quoted address: '%s'\n", faddr);
6311                         }
6312                 }
6313                 if (result == NULL)
6314                         result = g_strdup((faddr != NULL)? faddr: spec);
6315                 else {
6316                         result = g_strconcat(result,
6317                                              ", ",
6318                                              (faddr != NULL)? faddr: spec,
6319                                              NULL);
6320                 }
6321                 if (faddr != NULL) {
6322                         g_free(faddr);
6323                         faddr = NULL;
6324                 }
6325         }
6326         slist_free_strings_full(list);
6327
6328         return result;
6329 }
6330
6331 #define IS_IN_CUSTOM_HEADER(header) \
6332         (compose->account->add_customhdr && \
6333          custom_header_find(compose->account->customhdr_list, header) != NULL)
6334
6335 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6336                                                     GString *header, 
6337                                                     const gchar *fieldname,
6338                                                     const gchar *seperator)
6339 {
6340         gchar *str, *fieldname_w_colon;
6341         gboolean add_field = FALSE;
6342         GSList *list;
6343         ComposeHeaderEntry *headerentry;
6344         const gchar *headerentryname;
6345         const gchar *trans_fieldname;
6346         GString *fieldstr;
6347
6348         if (IS_IN_CUSTOM_HEADER(fieldname))
6349                 return;
6350
6351         debug_print("Adding %s-fields\n", fieldname);
6352
6353         fieldstr = g_string_sized_new(64);
6354
6355         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6356         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6357
6358         for (list = compose->header_list; list; list = list->next) {
6359                 headerentry = ((ComposeHeaderEntry *)list->data);
6360                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6361
6362                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6363                         gchar * ustr = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6364                         g_strstrip(ustr);
6365                         str = compose_quote_list_of_addresses(ustr);
6366                         g_free(ustr);
6367                         if (str != NULL && str[0] != '\0') {
6368                                 if (add_field)
6369                                         g_string_append(fieldstr, seperator);
6370                                 g_string_append(fieldstr, str);
6371                                 add_field = TRUE;
6372                         }
6373                         g_free(str);
6374                 }
6375         }
6376         if (add_field) {
6377                 gchar *buf;
6378
6379                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6380                 compose_convert_header
6381                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6382                         strlen(fieldname) + 2, TRUE);
6383                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6384                 g_free(buf);
6385         }
6386
6387         g_free(fieldname_w_colon);
6388         g_string_free(fieldstr, TRUE);
6389
6390         return;
6391 }
6392
6393 static gchar *compose_get_manual_headers_info(Compose *compose)
6394 {
6395         GString *sh_header = g_string_new(" ");
6396         GSList *list;
6397         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6398
6399         for (list = compose->header_list; list; list = list->next) {
6400                 ComposeHeaderEntry *headerentry;
6401                 gchar *tmp;
6402                 gchar *headername;
6403                 gchar *headername_wcolon;
6404                 const gchar *headername_trans;
6405                 gchar **string;
6406                 gboolean standard_header = FALSE;
6407
6408                 headerentry = ((ComposeHeaderEntry *)list->data);
6409
6410                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6411                 g_strstrip(tmp);
6412                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6413                         g_free(tmp);
6414                         continue;
6415                 }
6416
6417                 if (!strstr(tmp, ":")) {
6418                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6419                         headername = g_strdup(tmp);
6420                 } else {
6421                         headername_wcolon = g_strdup(tmp);
6422                         headername = g_strdup(strtok(tmp, ":"));
6423                 }
6424                 g_free(tmp);
6425                 
6426                 string = std_headers;
6427                 while (*string != NULL) {
6428                         headername_trans = prefs_common_translated_header_name(*string);
6429                         if (!strcmp(headername_trans, headername_wcolon))
6430                                 standard_header = TRUE;
6431                         string++;
6432                 }
6433                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6434                         g_string_append_printf(sh_header, "%s ", headername);
6435                 g_free(headername);
6436                 g_free(headername_wcolon);
6437         }
6438         g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6439         return g_string_free(sh_header, FALSE);
6440 }
6441
6442 static gchar *compose_get_header(Compose *compose)
6443 {
6444         gchar buf[BUFFSIZE];
6445         const gchar *entry_str;
6446         gchar *str;
6447         gchar *name;
6448         GSList *list;
6449         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6450         GString *header;
6451         gchar *from_name = NULL, *from_address = NULL;
6452         gchar *tmp;
6453
6454         cm_return_val_if_fail(compose->account != NULL, NULL);
6455         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6456
6457         header = g_string_sized_new(64);
6458
6459         /* Date */
6460         if (prefs_common.hide_timezone)
6461                 get_rfc822_date_hide_tz(buf, sizeof(buf));
6462         else
6463                 get_rfc822_date(buf, sizeof(buf));
6464         g_string_append_printf(header, "Date: %s\n", buf);
6465
6466         /* From */
6467         
6468         if (compose->account->name && *compose->account->name) {
6469                 gchar *buf;
6470                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6471                 tmp = g_strdup_printf("%s <%s>",
6472                         buf, compose->account->address);
6473         } else {
6474                 tmp = g_strdup_printf("%s",
6475                         compose->account->address);
6476         }
6477         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6478         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6479                 /* use default */
6480                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6481                 from_address = g_strdup(compose->account->address);
6482         } else {
6483                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6484                 /* extract name and address */
6485                 if (strstr(spec, " <") && strstr(spec, ">")) {
6486                         from_address = g_strdup(strrchr(spec, '<')+1);
6487                         *(strrchr(from_address, '>')) = '\0';
6488                         from_name = g_strdup(spec);
6489                         *(strrchr(from_name, '<')) = '\0';
6490                 } else {
6491                         from_name = NULL;
6492                         from_address = g_strdup(spec);
6493                 }
6494                 g_free(spec);
6495         }
6496         g_free(tmp);
6497         
6498         
6499         if (from_name && *from_name) {
6500                 gchar *qname;
6501                 compose_convert_header
6502                         (compose, buf, sizeof(buf), from_name,
6503                          strlen("From: "), TRUE);
6504                 QUOTE_IF_REQUIRED(name, buf);
6505                 qname = escape_internal_quotes(name, '"');
6506                 
6507                 g_string_append_printf(header, "From: %s <%s>\n",
6508                         qname, from_address);
6509                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6510                     compose->return_receipt) {
6511                         compose_convert_header(compose, buf, sizeof(buf), from_name,
6512                                                strlen("Disposition-Notification-To: "),
6513                                                TRUE);
6514                         g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, from_address);
6515                 }
6516                 if (qname != name)
6517                         g_free(qname);
6518         } else {
6519                 g_string_append_printf(header, "From: %s\n", from_address);
6520                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6521                     compose->return_receipt)
6522                         g_string_append_printf(header, "Disposition-Notification-To: %s\n", from_address);
6523
6524         }
6525         g_free(from_name);
6526         g_free(from_address);
6527
6528         /* To */
6529         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6530
6531         /* Newsgroups */
6532         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6533
6534         /* Cc */
6535         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6536
6537         /* Bcc */
6538         /* 
6539          * If this account is a NNTP account remove Bcc header from 
6540          * message body since it otherwise will be publicly shown
6541          */
6542         if (compose->account->protocol != A_NNTP)
6543                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6544
6545         /* Subject */
6546         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6547
6548         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6549                 g_strstrip(str);
6550                 if (*str != '\0') {
6551                         compose_convert_header(compose, buf, sizeof(buf), str,
6552                                                strlen("Subject: "), FALSE);
6553                         g_string_append_printf(header, "Subject: %s\n", buf);
6554                 }
6555         }
6556         g_free(str);
6557
6558         /* Message-ID */
6559         if (compose->msgid != NULL && strlen(compose->msgid) > 0) {
6560                 g_string_append_printf(header, "Message-ID: <%s>\n",
6561                                 compose->msgid);
6562         }
6563
6564         if (compose->remove_references == FALSE) {
6565                 /* In-Reply-To */
6566                 if (compose->inreplyto && compose->to_list)
6567                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6568         
6569                 /* References */
6570                 if (compose->references)
6571                         g_string_append_printf(header, "References: %s\n", compose->references);
6572         }
6573
6574         /* Followup-To */
6575         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6576
6577         /* Reply-To */
6578         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6579
6580         /* Organization */
6581         if (compose->account->organization &&
6582             strlen(compose->account->organization) &&
6583             !IS_IN_CUSTOM_HEADER("Organization")) {
6584                 compose_convert_header(compose, buf, sizeof(buf),
6585                                        compose->account->organization,
6586                                        strlen("Organization: "), FALSE);
6587                 g_string_append_printf(header, "Organization: %s\n", buf);
6588         }
6589
6590         /* Program version and system info */
6591         if (compose->account->gen_xmailer &&
6592             g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6593             !compose->newsgroup_list) {
6594                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6595                         prog_version,
6596                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6597                         TARGET_ALIAS);
6598         }
6599         if (compose->account->gen_xmailer &&
6600             g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6601                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6602                         prog_version,
6603                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6604                         TARGET_ALIAS);
6605         }
6606
6607         /* custom headers */
6608         if (compose->account->add_customhdr) {
6609                 GSList *cur;
6610
6611                 for (cur = compose->account->customhdr_list; cur != NULL;
6612                      cur = cur->next) {
6613                         CustomHeader *chdr = (CustomHeader *)cur->data;
6614
6615                         if (custom_header_is_allowed(chdr->name)
6616                             && chdr->value != NULL
6617                             && *(chdr->value) != '\0') {
6618                                 compose_convert_header
6619                                         (compose, buf, sizeof(buf),
6620                                          chdr->value,
6621                                          strlen(chdr->name) + 2, FALSE);
6622                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6623                         }
6624                 }
6625         }
6626
6627         /* Automatic Faces and X-Faces */
6628         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6629                 g_string_append_printf(header, "X-Face: %s\n", buf);
6630         }
6631         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6632                 g_string_append_printf(header, "X-Face: %s\n", buf);
6633         }
6634         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6635                 g_string_append_printf(header, "Face: %s\n", buf);
6636         }
6637         else if (get_default_face (buf, sizeof(buf)) == 0) {
6638                 g_string_append_printf(header, "Face: %s\n", buf);
6639         }
6640
6641         /* PRIORITY */
6642         switch (compose->priority) {
6643                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6644                                                    "X-Priority: 1 (Highest)\n");
6645                         break;
6646                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6647                                                 "X-Priority: 2 (High)\n");
6648                         break;
6649                 case PRIORITY_NORMAL: break;
6650                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6651                                                "X-Priority: 4 (Low)\n");
6652                         break;
6653                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6654                                                   "X-Priority: 5 (Lowest)\n");
6655                         break;
6656                 default: debug_print("compose: priority unknown : %d\n",
6657                                      compose->priority);
6658         }
6659
6660         /* get special headers */
6661         for (list = compose->header_list; list; list = list->next) {
6662                 ComposeHeaderEntry *headerentry;
6663                 gchar *tmp;
6664                 gchar *headername;
6665                 gchar *headername_wcolon;
6666                 const gchar *headername_trans;
6667                 gchar *headervalue;
6668                 gchar **string;
6669                 gboolean standard_header = FALSE;
6670
6671                 headerentry = ((ComposeHeaderEntry *)list->data);
6672
6673                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6674                 g_strstrip(tmp);
6675                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6676                         g_free(tmp);
6677                         continue;
6678                 }
6679
6680                 if (!strstr(tmp, ":")) {
6681                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6682                         headername = g_strdup(tmp);
6683                 } else {
6684                         headername_wcolon = g_strdup(tmp);
6685                         headername = g_strdup(strtok(tmp, ":"));
6686                 }
6687                 g_free(tmp);
6688                 
6689                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6690                 Xstrdup_a(headervalue, entry_str, return NULL);
6691                 subst_char(headervalue, '\r', ' ');
6692                 subst_char(headervalue, '\n', ' ');
6693                 string = std_headers;
6694                 while (*string != NULL) {
6695                         headername_trans = prefs_common_translated_header_name(*string);
6696                         if (!strcmp(headername_trans, headername_wcolon))
6697                                 standard_header = TRUE;
6698                         string++;
6699                 }
6700                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6701                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6702                                 
6703                 g_free(headername);
6704                 g_free(headername_wcolon);              
6705         }
6706
6707         str = header->str;
6708         g_string_free(header, FALSE);
6709
6710         return str;
6711 }
6712
6713 #undef IS_IN_CUSTOM_HEADER
6714
6715 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6716                                    gint header_len, gboolean addr_field)
6717 {
6718         gchar *tmpstr = NULL;
6719         const gchar *out_codeset = NULL;
6720
6721         cm_return_if_fail(src != NULL);
6722         cm_return_if_fail(dest != NULL);
6723
6724         if (len < 1) return;
6725
6726         tmpstr = g_strdup(src);
6727
6728         subst_char(tmpstr, '\n', ' ');
6729         subst_char(tmpstr, '\r', ' ');
6730         g_strchomp(tmpstr);
6731
6732         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6733                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6734                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6735                 g_free(tmpstr);
6736                 tmpstr = mybuf;
6737         }
6738
6739         codeconv_set_strict(TRUE);
6740         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6741                 conv_get_charset_str(compose->out_encoding));
6742         codeconv_set_strict(FALSE);
6743         
6744         if (!dest || *dest == '\0') {
6745                 gchar *test_conv_global_out = NULL;
6746                 gchar *test_conv_reply = NULL;
6747
6748                 /* automatic mode. be automatic. */
6749                 codeconv_set_strict(TRUE);
6750
6751                 out_codeset = conv_get_outgoing_charset_str();
6752                 if (out_codeset) {
6753                         debug_print("trying to convert to %s\n", out_codeset);
6754                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6755                 }
6756
6757                 if (!test_conv_global_out && compose->orig_charset
6758                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6759                         out_codeset = compose->orig_charset;
6760                         debug_print("failure; trying to convert to %s\n", out_codeset);
6761                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6762                 }
6763
6764                 if (!test_conv_global_out && !test_conv_reply) {
6765                         /* we're lost */
6766                         out_codeset = CS_INTERNAL;
6767                         debug_print("finally using %s\n", out_codeset);
6768                 }
6769                 g_free(test_conv_global_out);
6770                 g_free(test_conv_reply);
6771                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6772                                         out_codeset);
6773                 codeconv_set_strict(FALSE);
6774         }
6775         g_free(tmpstr);
6776 }
6777
6778 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6779 {
6780         gchar *address;
6781
6782         cm_return_if_fail(user_data != NULL);
6783
6784         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6785         g_strstrip(address);
6786         if (*address != '\0') {
6787                 gchar *name = procheader_get_fromname(address);
6788                 extract_address(address);
6789 #ifndef USE_ALT_ADDRBOOK
6790                 addressbook_add_contact(name, address, NULL, NULL);
6791 #else
6792                 debug_print("%s: %s\n", name, address);
6793                 if (addressadd_selection(name, address, NULL, NULL)) {
6794                         debug_print( "addressbook_add_contact - added\n" );
6795                 }
6796 #endif
6797         }
6798         g_free(address);
6799 }
6800
6801 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6802 {
6803         GtkWidget *menuitem;
6804         gchar *address;
6805
6806         cm_return_if_fail(menu != NULL);
6807         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6808
6809         menuitem = gtk_separator_menu_item_new();
6810         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6811         gtk_widget_show(menuitem);
6812
6813         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6814         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6815
6816         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6817         g_strstrip(address);
6818         if (*address == '\0') {
6819                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6820         }
6821
6822         g_signal_connect(G_OBJECT(menuitem), "activate",
6823                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6824         gtk_widget_show(menuitem);
6825 }
6826
6827 void compose_add_extra_header(gchar *header, GtkListStore *model)
6828 {
6829         GtkTreeIter iter;
6830         if (strcmp(header, "")) {
6831                 COMBOBOX_ADD(model, header, COMPOSE_TO);
6832         }
6833 }
6834
6835 void compose_add_extra_header_entries(GtkListStore *model)
6836 {
6837         FILE *exh;
6838         gchar *exhrc;
6839         gchar buf[BUFFSIZE];
6840         gint lastc;
6841
6842         if (extra_headers == NULL) {
6843                 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6844                 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6845                         debug_print("extra headers file not found\n");
6846                         goto extra_headers_done;
6847                 }
6848                 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6849                         lastc = strlen(buf) - 1;        /* remove trailing control chars */
6850                         while (lastc >= 0 && buf[lastc] != ':')
6851                                 buf[lastc--] = '\0';
6852                         if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6853                                 buf[lastc] = '\0'; /* remove trailing : for comparison */
6854                                 if (custom_header_is_allowed(buf)) {
6855                                         buf[lastc] = ':';
6856                                         extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6857                                 }
6858                                 else
6859                                         g_message("disallowed extra header line: %s\n", buf);
6860                         }
6861                         else {
6862                                 if (buf[0] != '#')
6863                                         g_message("invalid extra header line: %s\n", buf);
6864                         }
6865                 }
6866                 fclose(exh);
6867 extra_headers_done:
6868                 g_free(exhrc);
6869                 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6870                 extra_headers = g_slist_reverse(extra_headers);
6871         }
6872         g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6873 }
6874
6875 static void compose_create_header_entry(Compose *compose) 
6876 {
6877         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6878
6879         GtkWidget *combo;
6880         GtkWidget *entry;
6881         GtkWidget *button;
6882         GtkWidget *hbox;
6883         gchar **string;
6884         const gchar *header = NULL;
6885         ComposeHeaderEntry *headerentry;
6886         gboolean standard_header = FALSE;
6887         GtkListStore *model;
6888         GtkTreeIter iter;
6889         
6890         headerentry = g_new0(ComposeHeaderEntry, 1);
6891
6892         /* Combo box model */
6893         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6894 #if !GTK_CHECK_VERSION(2, 24, 0)
6895         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6896 #endif
6897         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6898                         COMPOSE_TO);
6899         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6900                         COMPOSE_CC);
6901         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6902                         COMPOSE_BCC);
6903         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6904                         COMPOSE_NEWSGROUPS);                    
6905         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6906                         COMPOSE_REPLYTO);
6907         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6908                         COMPOSE_FOLLOWUPTO);
6909         compose_add_extra_header_entries(model);
6910
6911         /* Combo box */
6912 #if GTK_CHECK_VERSION(2, 24, 0)
6913         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6914         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6915         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6916         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6917         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6918 #endif
6919         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6920         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6921                          G_CALLBACK(compose_grab_focus_cb), compose);
6922         gtk_widget_show(combo);
6923
6924         /* Putting only the combobox child into focus chain of its parent causes
6925          * the parent to be skipped when changing focus via Tab or Shift+Tab.
6926          * This eliminates need to pres Tab twice in order to really get from the
6927          * combobox to next widget. */
6928         GList *l = NULL;
6929         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6930         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6931         g_list_free(l);
6932
6933         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6934                         compose->header_nextrow, compose->header_nextrow+1,
6935                         GTK_SHRINK, GTK_FILL, 0, 0);
6936         if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
6937                 const gchar *last_header_entry = gtk_entry_get_text(
6938                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6939                 string = headers;
6940                 while (*string != NULL) {
6941                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6942                                 standard_header = TRUE;
6943                         string++;
6944                 }
6945                 if (standard_header)
6946                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6947         }
6948         if (!compose->header_last || !standard_header) {
6949                 switch(compose->account->protocol) {
6950                         case A_NNTP:
6951                                 header = prefs_common_translated_header_name("Newsgroups:");
6952                                 break;
6953                         default:
6954                                 header = prefs_common_translated_header_name("To:");
6955                                 break;
6956                 }                                                                   
6957         }
6958         if (header)
6959                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6960
6961         gtk_editable_set_editable(
6962                 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo)))),
6963                 prefs_common.type_any_header);
6964
6965         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6966                          G_CALLBACK(compose_grab_focus_cb), compose);
6967
6968         /* Entry field with cleanup button */
6969         button = gtk_button_new();
6970         gtk_button_set_image(GTK_BUTTON(button),
6971                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6972         gtk_widget_show(button);
6973         CLAWS_SET_TIP(button,
6974                 _("Delete entry contents"));
6975         entry = gtk_entry_new(); 
6976         gtk_widget_show(entry);
6977         CLAWS_SET_TIP(entry,
6978                 _("Use <tab> to autocomplete from addressbook"));
6979         hbox = gtk_hbox_new (FALSE, 0);
6980         gtk_widget_show(hbox);
6981         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6982         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6983         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6984                         compose->header_nextrow, compose->header_nextrow+1,
6985                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6986
6987         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6988                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6989                          headerentry);
6990         g_signal_connect(G_OBJECT(entry), "changed", 
6991                          G_CALLBACK(compose_headerentry_changed_cb), 
6992                          headerentry);
6993         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6994                          G_CALLBACK(compose_grab_focus_cb), compose);
6995
6996         g_signal_connect(G_OBJECT(button), "clicked",
6997                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6998                          headerentry); 
6999                          
7000         /* email dnd */
7001         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7002                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7003                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7004         g_signal_connect(G_OBJECT(entry), "drag_data_received",
7005                          G_CALLBACK(compose_header_drag_received_cb),
7006                          entry);
7007         g_signal_connect(G_OBJECT(entry), "drag-drop",
7008                          G_CALLBACK(compose_drag_drop),
7009                          compose);
7010         g_signal_connect(G_OBJECT(entry), "populate-popup",
7011                          G_CALLBACK(compose_entry_popup_extend),
7012                          NULL);
7013         
7014         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
7015
7016         headerentry->compose = compose;
7017         headerentry->combo = combo;
7018         headerentry->entry = entry;
7019         headerentry->button = button;
7020         headerentry->hbox = hbox;
7021         headerentry->headernum = compose->header_nextrow;
7022         headerentry->type = PREF_NONE;
7023
7024         compose->header_nextrow++;
7025         compose->header_last = headerentry;             
7026         compose->header_list =
7027                 g_slist_append(compose->header_list,
7028                                headerentry);
7029 }
7030
7031 static void compose_add_header_entry(Compose *compose, const gchar *header,
7032                                 gchar *text, ComposePrefType pref_type) 
7033 {
7034         ComposeHeaderEntry *last_header = compose->header_last;
7035         gchar *tmp = g_strdup(text), *email;
7036         gboolean replyto_hdr;
7037         
7038         replyto_hdr = (!strcasecmp(header,
7039                                 prefs_common_translated_header_name("Reply-To:")) ||
7040                         !strcasecmp(header,
7041                                 prefs_common_translated_header_name("Followup-To:")) ||
7042                         !strcasecmp(header,
7043                                 prefs_common_translated_header_name("In-Reply-To:")));
7044                 
7045         extract_address(tmp);
7046         email = g_utf8_strdown(tmp, -1);
7047         
7048         if (replyto_hdr == FALSE &&
7049             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
7050         {
7051                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7052                                 header, text, (gint) pref_type);
7053                 g_free(email);
7054                 g_free(tmp);
7055                 return;
7056         }
7057         
7058         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
7059                 gtk_entry_set_text(GTK_ENTRY(
7060                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
7061         else
7062                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
7063         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
7064         last_header->type = pref_type;
7065
7066         if (replyto_hdr == FALSE)
7067                 g_hash_table_insert(compose->email_hashtable, email,
7068                                     GUINT_TO_POINTER(1));
7069         else
7070                 g_free(email);
7071         
7072         g_free(tmp);
7073 }
7074
7075 static void compose_destroy_headerentry(Compose *compose, 
7076                                         ComposeHeaderEntry *headerentry)
7077 {
7078         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7079         gchar *email;
7080
7081         extract_address(text);
7082         email = g_utf8_strdown(text, -1);
7083         g_hash_table_remove(compose->email_hashtable, email);
7084         g_free(text);
7085         g_free(email);
7086         
7087         gtk_widget_destroy(headerentry->combo);
7088         gtk_widget_destroy(headerentry->entry);
7089         gtk_widget_destroy(headerentry->button);
7090         gtk_widget_destroy(headerentry->hbox);
7091         g_free(headerentry);
7092 }
7093
7094 static void compose_remove_header_entries(Compose *compose) 
7095 {
7096         GSList *list;
7097         for (list = compose->header_list; list; list = list->next)
7098                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7099
7100         compose->header_last = NULL;
7101         g_slist_free(compose->header_list);
7102         compose->header_list = NULL;
7103         compose->header_nextrow = 1;
7104         compose_create_header_entry(compose);
7105 }
7106
7107 static GtkWidget *compose_create_header(Compose *compose) 
7108 {
7109         GtkWidget *from_optmenu_hbox;
7110         GtkWidget *header_table_main;
7111         GtkWidget *header_scrolledwin;
7112         GtkWidget *header_table;
7113
7114         /* parent with account selection and from header */
7115         header_table_main = gtk_table_new(2, 2, FALSE);
7116         gtk_widget_show(header_table_main);
7117         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7118
7119         from_optmenu_hbox = compose_account_option_menu_create(compose);
7120         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7121                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7122
7123         /* child with header labels and entries */
7124         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7125         gtk_widget_show(header_scrolledwin);
7126         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7127
7128         header_table = gtk_table_new(2, 2, FALSE);
7129         gtk_widget_show(header_table);
7130         gtk_container_set_border_width(GTK_CONTAINER(header_table), 0);
7131         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7132         gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table),
7133                         gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin)));
7134         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin))), GTK_SHADOW_NONE);
7135
7136         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7137                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7138
7139         compose->header_table = header_table;
7140         compose->header_list = NULL;
7141         compose->header_nextrow = 0;
7142
7143         compose_create_header_entry(compose);
7144
7145         compose->table = NULL;
7146
7147         return header_table_main;
7148 }
7149
7150 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7151 {
7152         Compose *compose = (Compose *)data;
7153         GdkEventButton event;
7154         
7155         event.button = 3;
7156         event.time = gtk_get_current_event_time();
7157
7158         return attach_button_pressed(compose->attach_clist, &event, compose);
7159 }
7160
7161 static GtkWidget *compose_create_attach(Compose *compose)
7162 {
7163         GtkWidget *attach_scrwin;
7164         GtkWidget *attach_clist;
7165
7166         GtkListStore *store;
7167         GtkCellRenderer *renderer;
7168         GtkTreeViewColumn *column;
7169         GtkTreeSelection *selection;
7170
7171         /* attachment list */
7172         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7173         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7174                                        GTK_POLICY_AUTOMATIC,
7175                                        GTK_POLICY_AUTOMATIC);
7176         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7177
7178         store = gtk_list_store_new(N_ATTACH_COLS, 
7179                                    G_TYPE_STRING,
7180                                    G_TYPE_STRING,
7181                                    G_TYPE_STRING,
7182                                    G_TYPE_STRING,
7183                                    G_TYPE_POINTER,
7184                                    G_TYPE_AUTO_POINTER,
7185                                    -1);
7186         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7187                                         (GTK_TREE_MODEL(store)));
7188         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7189         g_object_unref(store);
7190         
7191         renderer = gtk_cell_renderer_text_new();
7192         column = gtk_tree_view_column_new_with_attributes
7193                         (_("Mime type"), renderer, "text", 
7194                          COL_MIMETYPE, NULL);
7195         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7196         
7197         renderer = gtk_cell_renderer_text_new();
7198         column = gtk_tree_view_column_new_with_attributes
7199                         (_("Size"), renderer, "text", 
7200                          COL_SIZE, NULL);
7201         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7202         
7203         renderer = gtk_cell_renderer_text_new();
7204         column = gtk_tree_view_column_new_with_attributes
7205                         (_("Name"), renderer, "text", 
7206                          COL_NAME, NULL);
7207         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7208
7209         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7210                                      prefs_common.use_stripes_everywhere);
7211         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7212         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7213
7214         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7215                          G_CALLBACK(attach_selected), compose);
7216         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7217                          G_CALLBACK(attach_button_pressed), compose);
7218         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7219                          G_CALLBACK(popup_attach_button_pressed), compose);
7220         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7221                          G_CALLBACK(attach_key_pressed), compose);
7222
7223         /* drag and drop */
7224         gtk_drag_dest_set(attach_clist,
7225                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7226                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7227                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7228         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7229                          G_CALLBACK(compose_attach_drag_received_cb),
7230                          compose);
7231         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7232                          G_CALLBACK(compose_drag_drop),
7233                          compose);
7234
7235         compose->attach_scrwin = attach_scrwin;
7236         compose->attach_clist  = attach_clist;
7237
7238         return attach_scrwin;
7239 }
7240
7241 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7242 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7243
7244 static GtkWidget *compose_create_others(Compose *compose)
7245 {
7246         GtkWidget *table;
7247         GtkWidget *savemsg_checkbtn;
7248         GtkWidget *savemsg_combo;
7249         GtkWidget *savemsg_select;
7250         
7251         guint rowcount = 0;
7252         gchar *folderidentifier;
7253
7254         /* Table for settings */
7255         table = gtk_table_new(3, 1, FALSE);
7256         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7257         gtk_widget_show(table);
7258         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7259         rowcount = 0;
7260
7261         /* Save Message to folder */
7262         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7263         gtk_widget_show(savemsg_checkbtn);
7264         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7265         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7266                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7267         }
7268         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7269                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7270
7271 #if !GTK_CHECK_VERSION(2, 24, 0)
7272         savemsg_combo = gtk_combo_box_entry_new_text();
7273 #else
7274         savemsg_combo = gtk_combo_box_text_new_with_entry();
7275 #endif
7276         compose->savemsg_checkbtn = savemsg_checkbtn;
7277         compose->savemsg_combo = savemsg_combo;
7278         gtk_widget_show(savemsg_combo);
7279
7280         if (prefs_common.compose_save_to_history)
7281 #if !GTK_CHECK_VERSION(2, 24, 0)
7282                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7283                                 prefs_common.compose_save_to_history);
7284 #else
7285                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7286                                 prefs_common.compose_save_to_history);
7287 #endif
7288         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7289         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7290         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7291                          G_CALLBACK(compose_grab_focus_cb), compose);
7292         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7293                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7294                                   (compose->account, F_OUTBOX));
7295                 compose_set_save_to(compose, folderidentifier);
7296                 g_free(folderidentifier);
7297         }
7298
7299         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7300         gtk_widget_show(savemsg_select);
7301         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7302         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7303                          G_CALLBACK(compose_savemsg_select_cb),
7304                          compose);
7305
7306         return table;   
7307 }
7308
7309 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7310 {
7311         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7312                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7313 }
7314
7315 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7316 {
7317         FolderItem *dest;
7318         gchar * path;
7319
7320         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7321         if (!dest) return;
7322
7323         path = folder_item_get_identifier(dest);
7324
7325         compose_set_save_to(compose, path);
7326         g_free(path);
7327 }
7328
7329 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7330                                   GdkAtom clip, GtkTextIter *insert_place);
7331
7332
7333 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7334                                        Compose *compose)
7335 {
7336         gint prev_autowrap;
7337         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7338 #if USE_ENCHANT
7339         if (event->button == 3) {
7340                 GtkTextIter iter;
7341                 GtkTextIter sel_start, sel_end;
7342                 gboolean stuff_selected;
7343                 gint x, y;
7344                 /* move the cursor to allow GtkAspell to check the word
7345                  * under the mouse */
7346                 if (event->x && event->y) {
7347                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7348                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7349                                 &x, &y);
7350                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7351                                 &iter, x, y);
7352                 } else {
7353                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7354                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7355                 }
7356                 /* get selection */
7357                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7358                                 buffer,
7359                                 &sel_start, &sel_end);
7360
7361                 gtk_text_buffer_place_cursor (buffer, &iter);
7362                 /* reselect stuff */
7363                 if (stuff_selected 
7364                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7365                         gtk_text_buffer_select_range(buffer,
7366                                 &sel_start, &sel_end);
7367                 }
7368                 return FALSE; /* pass the event so that the right-click goes through */
7369         }
7370 #endif
7371         if (event->button == 2) {
7372                 GtkTextIter iter;
7373                 gint x, y;
7374                 BLOCK_WRAP();
7375                 
7376                 /* get the middle-click position to paste at the correct place */
7377                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7378                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7379                         &x, &y);
7380                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7381                         &iter, x, y);
7382                 
7383                 entry_paste_clipboard(compose, text, 
7384                                 prefs_common.linewrap_pastes,
7385                                 GDK_SELECTION_PRIMARY, &iter);
7386                 UNBLOCK_WRAP();
7387                 return TRUE;
7388         }
7389         return FALSE;
7390 }
7391
7392 #if USE_ENCHANT
7393 static void compose_spell_menu_changed(void *data)
7394 {
7395         Compose *compose = (Compose *)data;
7396         GSList *items;
7397         GtkWidget *menuitem;
7398         GtkWidget *parent_item;
7399         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7400         GSList *spell_menu;
7401
7402         if (compose->gtkaspell == NULL)
7403                 return;
7404
7405         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7406                         "/Menu/Spelling/Options");
7407
7408         /* setting the submenu removes /Spelling/Options from the factory 
7409          * so we need to save it */
7410
7411         if (parent_item == NULL) {
7412                 parent_item = compose->aspell_options_menu;
7413                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7414         } else
7415                 compose->aspell_options_menu = parent_item;
7416
7417         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7418
7419         spell_menu = g_slist_reverse(spell_menu);
7420         for (items = spell_menu;
7421              items; items = items->next) {
7422                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7423                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7424                 gtk_widget_show(GTK_WIDGET(menuitem));
7425         }
7426         g_slist_free(spell_menu);
7427
7428         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7429         gtk_widget_show(parent_item);
7430 }
7431
7432 static void compose_dict_changed(void *data)
7433 {
7434         Compose *compose = (Compose *) data;
7435
7436         if(!compose->gtkaspell)
7437                 return; 
7438         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7439                 return;
7440
7441         gtkaspell_highlight_all(compose->gtkaspell);
7442         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7443 }
7444 #endif
7445
7446 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7447 {
7448         Compose *compose = (Compose *)data;
7449         GdkEventButton event;
7450         
7451         event.button = 3;
7452         event.time = gtk_get_current_event_time();
7453         event.x = 0;
7454         event.y = 0;
7455
7456         return text_clicked(compose->text, &event, compose);
7457 }
7458
7459 static gboolean compose_force_window_origin = TRUE;
7460 static Compose *compose_create(PrefsAccount *account,
7461                                                  FolderItem *folder,
7462                                                  ComposeMode mode,
7463                                                  gboolean batch)
7464 {
7465         Compose   *compose;
7466         GtkWidget *window;
7467         GtkWidget *vbox;
7468         GtkWidget *menubar;
7469         GtkWidget *handlebox;
7470
7471         GtkWidget *notebook;
7472         
7473         GtkWidget *attach_hbox;
7474         GtkWidget *attach_lab1;
7475         GtkWidget *attach_lab2;
7476
7477         GtkWidget *vbox2;
7478
7479         GtkWidget *label;
7480         GtkWidget *subject_hbox;
7481         GtkWidget *subject_frame;
7482         GtkWidget *subject_entry;
7483         GtkWidget *subject;
7484         GtkWidget *paned;
7485
7486         GtkWidget *edit_vbox;
7487         GtkWidget *ruler_hbox;
7488         GtkWidget *ruler;
7489         GtkWidget *scrolledwin;
7490         GtkWidget *text;
7491         GtkTextBuffer *buffer;
7492         GtkClipboard *clipboard;
7493
7494         UndoMain *undostruct;
7495
7496         GtkWidget *popupmenu;
7497         GtkWidget *tmpl_menu;
7498         GtkActionGroup *action_group = NULL;
7499
7500 #if USE_ENCHANT
7501         GtkAspell * gtkaspell = NULL;
7502 #endif
7503
7504         static GdkGeometry geometry;
7505
7506         cm_return_val_if_fail(account != NULL, NULL);
7507
7508         debug_print("Creating compose window...\n");
7509         compose = g_new0(Compose, 1);
7510
7511         compose->batch = batch;
7512         compose->account = account;
7513         compose->folder = folder;
7514         
7515         compose->mutex = cm_mutex_new();
7516         compose->set_cursor_pos = -1;
7517
7518         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7519
7520         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7521         gtk_widget_set_size_request(window, prefs_common.compose_width,
7522                                         prefs_common.compose_height);
7523
7524         if (!geometry.max_width) {
7525                 geometry.max_width = gdk_screen_width();
7526                 geometry.max_height = gdk_screen_height();
7527         }
7528
7529         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7530                                       &geometry, GDK_HINT_MAX_SIZE);
7531         if (!geometry.min_width) {
7532                 geometry.min_width = 600;
7533                 geometry.min_height = 440;
7534         }
7535         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7536                                       &geometry, GDK_HINT_MIN_SIZE);
7537
7538 #ifndef GENERIC_UMPC    
7539         if (compose_force_window_origin)
7540                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7541                                  prefs_common.compose_y);
7542 #endif
7543         g_signal_connect(G_OBJECT(window), "delete_event",
7544                          G_CALLBACK(compose_delete_cb), compose);
7545         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7546         gtk_widget_realize(window);
7547
7548         gtkut_widget_set_composer_icon(window);
7549
7550         vbox = gtk_vbox_new(FALSE, 0);
7551         gtk_container_add(GTK_CONTAINER(window), vbox);
7552
7553         compose->ui_manager = gtk_ui_manager_new();
7554         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7555                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7556         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7557                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7558         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7559                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7560         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7561                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7562         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7563                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7564
7565         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7566
7567         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7568         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7569 #ifdef USE_ENCHANT
7570         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7571 #endif
7572         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7573         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7574         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7575
7576 /* Compose menu */
7577         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7578         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7579         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7580         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7581         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7582         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7583         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7584         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7585         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7586         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7587         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7588         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7589         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7590
7591 /* Edit menu */
7592         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7593         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7594         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7595
7596         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7597         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7598         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7599
7600         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7601         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7602         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7603         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7604
7605         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7606
7607         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7608         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7609         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7610         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7611         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7612         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7613         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7614         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7615         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7616         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7617         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7618         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7619         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7620         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7621         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7622
7623         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7624
7625         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7626         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7627         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7628         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7629         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7630
7631         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7632
7633         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7634
7635 #if USE_ENCHANT
7636 /* Spelling menu */
7637         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7638         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7639         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7640         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7641         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7642         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7643 #endif
7644
7645 /* Options menu */
7646         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7647         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7648         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7649         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7650         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7651
7652         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7653         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7654         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7655         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7656         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7657
7658         
7659         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7660         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7661         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7662         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7663         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7664         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7665         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7666
7667         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7668         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7669         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7670         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7671         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7672
7673         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7674
7675         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7676         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7677         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7678         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7679         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7680
7681         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7682         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)
7683         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)
7684         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7685
7686         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7687
7688         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7689         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)
7690         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)
7691
7692         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7693
7694         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7695         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)
7696         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7697
7698         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7699         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)
7700         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7701
7702         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7703
7704         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7705         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)
7706         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7707         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR, "Options/Encoding/Cyrillic/"CS_MACCYR, GTK_UI_MANAGER_MENUITEM)
7708         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7709         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7710
7711         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7712         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)
7713         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)
7714         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7715         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7716
7717         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7718         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7719         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7720         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7721         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7722         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7723
7724         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7725         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7726         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)
7727
7728         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7729         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7730         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7731 /* phew. */
7732
7733 /* Tools menu */
7734         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7735         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7736         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7737         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7738         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7739         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7740
7741 /* Help menu */
7742         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7743
7744         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7745         gtk_widget_show_all(menubar);
7746
7747         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7748         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7749
7750         if (prefs_common.toolbar_detachable) {
7751                 handlebox = gtk_handle_box_new();
7752         } else {
7753                 handlebox = gtk_hbox_new(FALSE, 0);
7754         }
7755         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7756
7757         gtk_widget_realize(handlebox);
7758         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7759                                           (gpointer)compose);
7760
7761         vbox2 = gtk_vbox_new(FALSE, 2);
7762         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7763         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7764         
7765         /* Notebook */
7766         notebook = gtk_notebook_new();
7767         gtk_widget_show(notebook);
7768
7769         /* header labels and entries */
7770         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7771                         compose_create_header(compose),
7772                         gtk_label_new_with_mnemonic(_("Hea_der")));
7773         /* attachment list */
7774         attach_hbox = gtk_hbox_new(FALSE, 0);
7775         gtk_widget_show(attach_hbox);
7776         
7777         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7778         gtk_widget_show(attach_lab1);
7779         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7780         
7781         attach_lab2 = gtk_label_new("");
7782         gtk_widget_show(attach_lab2);
7783         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7784         
7785         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7786                         compose_create_attach(compose),
7787                         attach_hbox);
7788         /* Others Tab */
7789         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7790                         compose_create_others(compose),
7791                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7792
7793         /* Subject */
7794         subject_hbox = gtk_hbox_new(FALSE, 0);
7795         gtk_widget_show(subject_hbox);
7796
7797         subject_frame = gtk_frame_new(NULL);
7798         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7799         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7800         gtk_widget_show(subject_frame);
7801
7802         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7803         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7804         gtk_widget_show(subject);
7805
7806         label = gtk_label_new_with_mnemonic(_("S_ubject:"));
7807         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7808         gtk_widget_show(label);
7809
7810 #ifdef USE_ENCHANT
7811         subject_entry = claws_spell_entry_new();
7812 #else
7813         subject_entry = gtk_entry_new();
7814 #endif
7815         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7816         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7817                          G_CALLBACK(compose_grab_focus_cb), compose);
7818         gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
7819         gtk_widget_show(subject_entry);
7820         compose->subject_entry = subject_entry;
7821         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7822         
7823         edit_vbox = gtk_vbox_new(FALSE, 0);
7824
7825         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7826
7827         /* ruler */
7828         ruler_hbox = gtk_hbox_new(FALSE, 0);
7829         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7830
7831         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7832         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7833         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7834                            BORDER_WIDTH);
7835
7836         /* text widget */
7837         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7838         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7839                                        GTK_POLICY_AUTOMATIC,
7840                                        GTK_POLICY_AUTOMATIC);
7841         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7842                                             GTK_SHADOW_IN);
7843         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7844
7845         text = gtk_text_view_new();
7846         if (prefs_common.show_compose_margin) {
7847                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7848                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7849         }
7850         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7851         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7852         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7853         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7854         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7855         
7856         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7857         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7858                                G_CALLBACK(compose_edit_size_alloc),
7859                                ruler);
7860         g_signal_connect(G_OBJECT(buffer), "changed",
7861                          G_CALLBACK(compose_changed_cb), compose);
7862         g_signal_connect(G_OBJECT(text), "grab_focus",
7863                          G_CALLBACK(compose_grab_focus_cb), compose);
7864         g_signal_connect(G_OBJECT(buffer), "insert_text",
7865                          G_CALLBACK(text_inserted), compose);
7866         g_signal_connect(G_OBJECT(text), "button_press_event",
7867                          G_CALLBACK(text_clicked), compose);
7868         g_signal_connect(G_OBJECT(text), "popup-menu",
7869                          G_CALLBACK(compose_popup_menu), compose);
7870         g_signal_connect(G_OBJECT(subject_entry), "changed",
7871                         G_CALLBACK(compose_changed_cb), compose);
7872         g_signal_connect(G_OBJECT(subject_entry), "activate",
7873                         G_CALLBACK(compose_subject_entry_activated), compose);
7874
7875         /* drag and drop */
7876         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7877                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7878                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7879         g_signal_connect(G_OBJECT(text), "drag_data_received",
7880                          G_CALLBACK(compose_insert_drag_received_cb),
7881                          compose);
7882         g_signal_connect(G_OBJECT(text), "drag-drop",
7883                          G_CALLBACK(compose_drag_drop),
7884                          compose);
7885         g_signal_connect(G_OBJECT(text), "key-press-event",
7886                          G_CALLBACK(completion_set_focus_to_subject),
7887                          compose);
7888         gtk_widget_show_all(vbox);
7889
7890         /* pane between attach clist and text */
7891         paned = gtk_vpaned_new();
7892         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7893         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
7894         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
7895         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
7896         g_signal_connect(G_OBJECT(notebook), "size_allocate",
7897                          G_CALLBACK(compose_notebook_size_alloc), paned);
7898
7899         gtk_widget_show_all(paned);
7900
7901
7902         if (prefs_common.textfont) {
7903                 PangoFontDescription *font_desc;
7904
7905                 font_desc = pango_font_description_from_string
7906                         (prefs_common.textfont);
7907                 if (font_desc) {
7908                         gtk_widget_modify_font(text, font_desc);
7909                         pango_font_description_free(font_desc);
7910                 }
7911         }
7912
7913         gtk_action_group_add_actions(action_group, compose_popup_entries,
7914                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7915         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7916         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7917         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7918         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7919         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7920         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7921         
7922         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7923
7924         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7925         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7926         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7927
7928         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7929
7930         undostruct = undo_init(text);
7931         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7932                                    compose);
7933
7934         address_completion_start(window);
7935
7936         compose->window        = window;
7937         compose->vbox          = vbox;
7938         compose->menubar       = menubar;
7939         compose->handlebox     = handlebox;
7940
7941         compose->vbox2         = vbox2;
7942
7943         compose->paned = paned;
7944
7945         compose->attach_label  = attach_lab2;
7946
7947         compose->notebook      = notebook;
7948         compose->edit_vbox     = edit_vbox;
7949         compose->ruler_hbox    = ruler_hbox;
7950         compose->ruler         = ruler;
7951         compose->scrolledwin   = scrolledwin;
7952         compose->text          = text;
7953
7954         compose->focused_editable = NULL;
7955
7956         compose->popupmenu    = popupmenu;
7957
7958         compose->tmpl_menu = tmpl_menu;
7959
7960         compose->mode = mode;
7961         compose->rmode = mode;
7962
7963         compose->targetinfo = NULL;
7964         compose->replyinfo  = NULL;
7965         compose->fwdinfo    = NULL;
7966
7967         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7968                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7969         
7970         compose->replyto     = NULL;
7971         compose->cc          = NULL;
7972         compose->bcc         = NULL;
7973         compose->followup_to = NULL;
7974
7975         compose->ml_post     = NULL;
7976
7977         compose->inreplyto   = NULL;
7978         compose->references  = NULL;
7979         compose->msgid       = NULL;
7980         compose->boundary    = NULL;
7981
7982         compose->autowrap       = prefs_common.autowrap;
7983         compose->autoindent     = prefs_common.auto_indent;
7984         compose->use_signing    = FALSE;
7985         compose->use_encryption = FALSE;
7986         compose->privacy_system = NULL;
7987         compose->encdata        = NULL;
7988
7989         compose->modified = FALSE;
7990
7991         compose->return_receipt = FALSE;
7992
7993         compose->to_list        = NULL;
7994         compose->newsgroup_list = NULL;
7995
7996         compose->undostruct = undostruct;
7997
7998         compose->sig_str = NULL;
7999
8000         compose->exteditor_file    = NULL;
8001         compose->exteditor_pid     = -1;
8002         compose->exteditor_tag     = -1;
8003         compose->exteditor_socket  = NULL;
8004         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
8005
8006         compose->folder_update_callback_id =
8007                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
8008                                 compose_update_folder_hook,
8009                                 (gpointer) compose);
8010
8011 #if USE_ENCHANT
8012         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
8013         if (mode != COMPOSE_REDIRECT) {
8014                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
8015                     strcmp(prefs_common.dictionary, "")) {
8016                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
8017                                                   prefs_common.alt_dictionary,
8018                                                   conv_get_locale_charset_str(),
8019                                                   prefs_common.misspelled_col,
8020                                                   prefs_common.check_while_typing,
8021                                                   prefs_common.recheck_when_changing_dict,
8022                                                   prefs_common.use_alternate,
8023                                                   prefs_common.use_both_dicts,
8024                                                   GTK_TEXT_VIEW(text),
8025                                                   GTK_WINDOW(compose->window),
8026                                                   compose_dict_changed,
8027                                                   compose_spell_menu_changed,
8028                                                   compose);
8029                         if (!gtkaspell) {
8030                                 alertpanel_error(_("Spell checker could not "
8031                                                 "be started.\n%s"),
8032                                                 gtkaspell_checkers_strerror());
8033                                 gtkaspell_checkers_reset_error();
8034                         } else {
8035                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
8036                         }
8037                 }
8038         }
8039         compose->gtkaspell = gtkaspell;
8040         compose_spell_menu_changed(compose);
8041         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
8042 #endif
8043
8044         compose_select_account(compose, account, TRUE);
8045
8046         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
8047         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
8048
8049         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
8050                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8051
8052         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
8053                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8054         
8055         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
8056                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8057
8058         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
8059         if (account->protocol != A_NNTP)
8060                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8061                                 prefs_common_translated_header_name("To:"));
8062         else
8063                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8064                                 prefs_common_translated_header_name("Newsgroups:"));
8065
8066 #ifndef USE_ALT_ADDRBOOK
8067         addressbook_set_target_compose(compose);
8068 #endif  
8069         if (mode != COMPOSE_REDIRECT)
8070                 compose_set_template_menu(compose);
8071         else {
8072                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8073         }
8074
8075         compose_list = g_list_append(compose_list, compose);
8076
8077         if (!prefs_common.show_ruler)
8078                 gtk_widget_hide(ruler_hbox);
8079                 
8080         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8081
8082         /* Priority */
8083         compose->priority = PRIORITY_NORMAL;
8084         compose_update_priority_menu_item(compose);
8085
8086         compose_set_out_encoding(compose);
8087         
8088         /* Actions menu */
8089         compose_update_actions_menu(compose);
8090
8091         /* Privacy Systems menu */
8092         compose_update_privacy_systems_menu(compose);
8093
8094         activate_privacy_system(compose, account, TRUE);
8095         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8096         if (batch) {
8097                 gtk_widget_realize(window);
8098         } else {
8099                 gtk_widget_show(window);
8100         }
8101         
8102         return compose;
8103 }
8104
8105 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8106 {
8107         GList *accounts;
8108         GtkWidget *hbox;
8109         GtkWidget *optmenu;
8110         GtkWidget *optmenubox;
8111         GtkWidget *fromlabel;
8112         GtkListStore *menu;
8113         GtkTreeIter iter;
8114         GtkWidget *from_name = NULL;
8115
8116         gint num = 0, def_menu = 0;
8117         
8118         accounts = account_get_list();
8119         cm_return_val_if_fail(accounts != NULL, NULL);
8120
8121         optmenubox = gtk_event_box_new();
8122         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8123         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8124
8125         hbox = gtk_hbox_new(FALSE, 4);
8126         from_name = gtk_entry_new();
8127         
8128         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8129                          G_CALLBACK(compose_grab_focus_cb), compose);
8130         g_signal_connect_after(G_OBJECT(from_name), "activate",
8131                          G_CALLBACK(from_name_activate_cb), optmenu);
8132
8133         for (; accounts != NULL; accounts = accounts->next, num++) {
8134                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8135                 gchar *name, *from = NULL;
8136
8137                 if (ac == compose->account) def_menu = num;
8138
8139                 name = g_markup_printf_escaped("<i>%s</i>",
8140                                        ac->account_name);
8141                 
8142                 if (ac == compose->account) {
8143                         if (ac->name && *ac->name) {
8144                                 gchar *buf;
8145                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8146                                 from = g_strdup_printf("%s <%s>",
8147                                                        buf, ac->address);
8148                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8149                         } else {
8150                                 from = g_strdup_printf("%s",
8151                                                        ac->address);
8152                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8153                         }
8154                 }
8155                 COMBOBOX_ADD(menu, name, ac->account_id);
8156                 g_free(name);
8157                 g_free(from);
8158         }
8159
8160         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8161
8162         g_signal_connect(G_OBJECT(optmenu), "changed",
8163                         G_CALLBACK(account_activated),
8164                         compose);
8165         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8166                          G_CALLBACK(compose_entry_popup_extend),
8167                          NULL);
8168
8169         fromlabel = gtk_label_new_with_mnemonic(_("_From:"));
8170         gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel), from_name);
8171
8172         gtk_box_pack_start(GTK_BOX(hbox), fromlabel, FALSE, FALSE, 4);
8173         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8174         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8175
8176         /* Putting only the GtkEntry into focus chain of parent hbox causes
8177          * the account selector combobox next to it to be unreachable when
8178          * navigating widgets in GtkTable with up/down arrow keys.
8179          * Note: gtk_widget_set_can_focus() was not enough. */
8180         GList *l = NULL;
8181         l = g_list_prepend(l, from_name);
8182         gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8183         g_list_free(l);
8184         
8185         CLAWS_SET_TIP(optmenubox,
8186                 _("Account to use for this email"));
8187         CLAWS_SET_TIP(from_name,
8188                 _("Sender address to be used"));
8189
8190         compose->account_combo = optmenu;
8191         compose->from_name = from_name;
8192         
8193         return hbox;
8194 }
8195
8196 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8197 {
8198         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8199         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8200         Compose *compose = (Compose *) data;
8201         if (active) {
8202                 compose->priority = value;
8203         }
8204 }
8205
8206 static void compose_reply_change_mode(Compose *compose,
8207                                     ComposeMode action)
8208 {
8209         gboolean was_modified = compose->modified;
8210
8211         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8212         
8213         cm_return_if_fail(compose->replyinfo != NULL);
8214         
8215         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8216                 ml = TRUE;
8217         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8218                 followup = TRUE;
8219         if (action == COMPOSE_REPLY_TO_ALL)
8220                 all = TRUE;
8221         if (action == COMPOSE_REPLY_TO_SENDER)
8222                 sender = TRUE;
8223         if (action == COMPOSE_REPLY_TO_LIST)
8224                 ml = TRUE;
8225
8226         compose_remove_header_entries(compose);
8227         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8228         if (compose->account->set_autocc && compose->account->auto_cc)
8229                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8230
8231         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8232                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8233         
8234         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8235                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8236         compose_show_first_last_header(compose, TRUE);
8237         compose->modified = was_modified;
8238         compose_set_title(compose);
8239 }
8240
8241 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8242 {
8243         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8244         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8245         Compose *compose = (Compose *) data;
8246         
8247         if (active)
8248                 compose_reply_change_mode(compose, value);
8249 }
8250
8251 static void compose_update_priority_menu_item(Compose * compose)
8252 {
8253         GtkWidget *menuitem = NULL;
8254         switch (compose->priority) {
8255                 case PRIORITY_HIGHEST:
8256                         menuitem = gtk_ui_manager_get_widget
8257                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8258                         break;
8259                 case PRIORITY_HIGH:
8260                         menuitem = gtk_ui_manager_get_widget
8261                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8262                         break;
8263                 case PRIORITY_NORMAL:
8264                         menuitem = gtk_ui_manager_get_widget
8265                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8266                         break;
8267                 case PRIORITY_LOW:
8268                         menuitem = gtk_ui_manager_get_widget
8269                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8270                         break;
8271                 case PRIORITY_LOWEST:
8272                         menuitem = gtk_ui_manager_get_widget
8273                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8274                         break;
8275         }
8276         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8277 }       
8278
8279 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8280 {
8281         Compose *compose = (Compose *) data;
8282         gchar *systemid;
8283         gboolean can_sign = FALSE, can_encrypt = FALSE;
8284
8285         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8286
8287         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8288                 return;
8289
8290         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8291         g_free(compose->privacy_system);
8292         compose->privacy_system = NULL;
8293         g_free(compose->encdata);
8294         compose->encdata = NULL;
8295         if (systemid != NULL) {
8296                 compose->privacy_system = g_strdup(systemid);
8297
8298                 can_sign = privacy_system_can_sign(systemid);
8299                 can_encrypt = privacy_system_can_encrypt(systemid);
8300         }
8301
8302         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8303
8304         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8305         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8306 }
8307
8308 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8309 {
8310         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8311         GtkWidget *menuitem = NULL;
8312         GList *children, *amenu;
8313         gboolean can_sign = FALSE, can_encrypt = FALSE;
8314         gboolean found = FALSE;
8315
8316         if (compose->privacy_system != NULL) {
8317                 gchar *systemid;
8318                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8319                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8320                 cm_return_if_fail(menuitem != NULL);
8321
8322                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8323                 amenu = children;
8324                 menuitem = NULL;
8325                 while (amenu != NULL) {
8326                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8327                         if (systemid != NULL) {
8328                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8329                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8330                                         menuitem = GTK_WIDGET(amenu->data);
8331
8332                                         can_sign = privacy_system_can_sign(systemid);
8333                                         can_encrypt = privacy_system_can_encrypt(systemid);
8334                                         found = TRUE;
8335                                         break;
8336                                 } 
8337                         } else if (strlen(compose->privacy_system) == 0 && 
8338                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8339                                         menuitem = GTK_WIDGET(amenu->data);
8340
8341                                         can_sign = FALSE;
8342                                         can_encrypt = FALSE;
8343                                         found = TRUE;
8344                                         break;
8345                         }
8346
8347                         amenu = amenu->next;
8348                 }
8349                 g_list_free(children);
8350                 if (menuitem != NULL)
8351                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8352                 
8353                 if (warn && !found && strlen(compose->privacy_system)) {
8354                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8355                                   "will not be able to sign or encrypt this message."),
8356                                   compose->privacy_system);
8357                 }
8358         } 
8359
8360         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8361         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8362 }       
8363  
8364 static void compose_set_out_encoding(Compose *compose)
8365 {
8366         CharSet out_encoding;
8367         const gchar *branch = NULL;
8368         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8369
8370         switch(out_encoding) {
8371                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8372                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8373                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8374                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8375                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8376                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8377                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8378                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8379                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8380                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8381                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8382                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8383                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8384                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8385                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8386                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8387                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8388                 case C_MACCYR: branch = "Menu/Options/Encoding/Cyrillic/" CS_MACCYR; break;
8389                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8390                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8391                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8392                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8393                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8394                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8395                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8396                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8397                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8398                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8399                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8400                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8401                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8402                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8403                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8404                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8405         }
8406         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8407 }
8408
8409 static void compose_set_template_menu(Compose *compose)
8410 {
8411         GSList *tmpl_list, *cur;
8412         GtkWidget *menu;
8413         GtkWidget *item;
8414
8415         tmpl_list = template_get_config();
8416
8417         menu = gtk_menu_new();
8418
8419         gtk_menu_set_accel_group (GTK_MENU (menu), 
8420                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8421         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8422                 Template *tmpl = (Template *)cur->data;
8423                 gchar *accel_path = NULL;
8424                 item = gtk_menu_item_new_with_label(tmpl->name);
8425                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8426                 g_signal_connect(G_OBJECT(item), "activate",
8427                                  G_CALLBACK(compose_template_activate_cb),
8428                                  compose);
8429                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8430                 gtk_widget_show(item);
8431                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8432                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8433                 g_free(accel_path);
8434         }
8435
8436         gtk_widget_show(menu);
8437         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8438 }
8439
8440 void compose_update_actions_menu(Compose *compose)
8441 {
8442         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8443 }
8444
8445 static void compose_update_privacy_systems_menu(Compose *compose)
8446 {
8447         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8448         GSList *systems, *cur;
8449         GtkWidget *widget;
8450         GtkWidget *system_none;
8451         GSList *group;
8452         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8453         GtkWidget *privacy_menu = gtk_menu_new();
8454
8455         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8456         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8457
8458         g_signal_connect(G_OBJECT(system_none), "activate",
8459                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8460
8461         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8462         gtk_widget_show(system_none);
8463
8464         systems = privacy_get_system_ids();
8465         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8466                 gchar *systemid = cur->data;
8467
8468                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8469                 widget = gtk_radio_menu_item_new_with_label(group,
8470                         privacy_system_get_name(systemid));
8471                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8472                                        g_strdup(systemid), g_free);
8473                 g_signal_connect(G_OBJECT(widget), "activate",
8474                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8475
8476                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8477                 gtk_widget_show(widget);
8478                 g_free(systemid);
8479         }
8480         g_slist_free(systems);
8481         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8482         gtk_widget_show_all(privacy_menu);
8483         gtk_widget_show_all(privacy_menuitem);
8484 }
8485
8486 void compose_reflect_prefs_all(void)
8487 {
8488         GList *cur;
8489         Compose *compose;
8490
8491         for (cur = compose_list; cur != NULL; cur = cur->next) {
8492                 compose = (Compose *)cur->data;
8493                 compose_set_template_menu(compose);
8494         }
8495 }
8496
8497 void compose_reflect_prefs_pixmap_theme(void)
8498 {
8499         GList *cur;
8500         Compose *compose;
8501
8502         for (cur = compose_list; cur != NULL; cur = cur->next) {
8503                 compose = (Compose *)cur->data;
8504                 toolbar_update(TOOLBAR_COMPOSE, compose);
8505         }
8506 }
8507
8508 static const gchar *compose_quote_char_from_context(Compose *compose)
8509 {
8510         const gchar *qmark = NULL;
8511
8512         cm_return_val_if_fail(compose != NULL, NULL);
8513
8514         switch (compose->mode) {
8515                 /* use forward-specific quote char */
8516                 case COMPOSE_FORWARD:
8517                 case COMPOSE_FORWARD_AS_ATTACH:
8518                 case COMPOSE_FORWARD_INLINE:
8519                         if (compose->folder && compose->folder->prefs &&
8520                                         compose->folder->prefs->forward_with_format)
8521                                 qmark = compose->folder->prefs->forward_quotemark;
8522                         else if (compose->account->forward_with_format)
8523                                 qmark = compose->account->forward_quotemark;
8524                         else
8525                                 qmark = prefs_common.fw_quotemark;
8526                         break;
8527
8528                 /* use reply-specific quote char in all other modes */
8529                 default:
8530                         if (compose->folder && compose->folder->prefs &&
8531                                         compose->folder->prefs->reply_with_format)
8532                                 qmark = compose->folder->prefs->reply_quotemark;
8533                         else if (compose->account->reply_with_format)
8534                                 qmark = compose->account->reply_quotemark;
8535                         else
8536                                 qmark = prefs_common.quotemark;
8537                         break;
8538         }
8539
8540         if (qmark == NULL || *qmark == '\0')
8541                 qmark = "> ";
8542
8543         return qmark;
8544 }
8545
8546 static void compose_template_apply(Compose *compose, Template *tmpl,
8547                                    gboolean replace)
8548 {
8549         GtkTextView *text;
8550         GtkTextBuffer *buffer;
8551         GtkTextMark *mark;
8552         GtkTextIter iter;
8553         const gchar *qmark;
8554         gchar *parsed_str = NULL;
8555         gint cursor_pos = 0;
8556         const gchar *err_msg = _("The body of the template has an error at line %d.");
8557         if (!tmpl) return;
8558
8559         /* process the body */
8560
8561         text = GTK_TEXT_VIEW(compose->text);
8562         buffer = gtk_text_view_get_buffer(text);
8563
8564         if (tmpl->value) {
8565                 qmark = compose_quote_char_from_context(compose);
8566
8567                 if (compose->replyinfo != NULL) {
8568
8569                         if (replace)
8570                                 gtk_text_buffer_set_text(buffer, "", -1);
8571                         mark = gtk_text_buffer_get_insert(buffer);
8572                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8573
8574                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8575                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8576
8577                 } else if (compose->fwdinfo != NULL) {
8578
8579                         if (replace)
8580                                 gtk_text_buffer_set_text(buffer, "", -1);
8581                         mark = gtk_text_buffer_get_insert(buffer);
8582                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8583
8584                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8585                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8586
8587                 } else {
8588                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8589
8590                         GtkTextIter start, end;
8591                         gchar *tmp = NULL;
8592
8593                         gtk_text_buffer_get_start_iter(buffer, &start);
8594                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8595                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8596
8597                         /* clear the buffer now */
8598                         if (replace)
8599                                 gtk_text_buffer_set_text(buffer, "", -1);
8600
8601                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8602                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8603                         procmsg_msginfo_free( &dummyinfo );
8604
8605                         g_free( tmp );
8606                 } 
8607         } else {
8608                 if (replace)
8609                         gtk_text_buffer_set_text(buffer, "", -1);
8610                 mark = gtk_text_buffer_get_insert(buffer);
8611                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8612         }       
8613
8614         if (replace && parsed_str && compose->account->auto_sig)
8615                 compose_insert_sig(compose, FALSE);
8616
8617         if (replace && parsed_str) {
8618                 gtk_text_buffer_get_start_iter(buffer, &iter);
8619                 gtk_text_buffer_place_cursor(buffer, &iter);
8620         }
8621         
8622         if (parsed_str) {
8623                 cursor_pos = quote_fmt_get_cursor_pos();
8624                 compose->set_cursor_pos = cursor_pos;
8625                 if (cursor_pos == -1)
8626                         cursor_pos = 0;
8627                 gtk_text_buffer_get_start_iter(buffer, &iter);
8628                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8629                 gtk_text_buffer_place_cursor(buffer, &iter);
8630         }
8631
8632         /* process the other fields */
8633
8634         compose_template_apply_fields(compose, tmpl);
8635         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8636         quote_fmt_reset_vartable();
8637         compose_changed_cb(NULL, compose);
8638
8639 #ifdef USE_ENCHANT
8640         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8641                 gtkaspell_highlight_all(compose->gtkaspell);
8642 #endif
8643 }
8644
8645 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8646 {
8647         MsgInfo* dummyinfo = NULL;
8648         MsgInfo *msginfo = NULL;
8649         gchar *buf = NULL;
8650
8651         if (compose->replyinfo != NULL)
8652                 msginfo = compose->replyinfo;
8653         else if (compose->fwdinfo != NULL)
8654                 msginfo = compose->fwdinfo;
8655         else {
8656                 dummyinfo = compose_msginfo_new_from_compose(compose);
8657                 msginfo = dummyinfo;
8658         }
8659
8660         if (tmpl->from && *tmpl->from != '\0') {
8661 #ifdef USE_ENCHANT
8662                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8663                                 compose->gtkaspell);
8664 #else
8665                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8666 #endif
8667                 quote_fmt_scan_string(tmpl->from);
8668                 quote_fmt_parse();
8669
8670                 buf = quote_fmt_get_buffer();
8671                 if (buf == NULL) {
8672                         alertpanel_error(_("Template From format error."));
8673                 } else {
8674                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8675                 }
8676         }
8677
8678         if (tmpl->to && *tmpl->to != '\0') {
8679 #ifdef USE_ENCHANT
8680                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8681                                 compose->gtkaspell);
8682 #else
8683                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8684 #endif
8685                 quote_fmt_scan_string(tmpl->to);
8686                 quote_fmt_parse();
8687
8688                 buf = quote_fmt_get_buffer();
8689                 if (buf == NULL) {
8690                         alertpanel_error(_("Template To format error."));
8691                 } else {
8692                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8693                 }
8694         }
8695
8696         if (tmpl->cc && *tmpl->cc != '\0') {
8697 #ifdef USE_ENCHANT
8698                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8699                                 compose->gtkaspell);
8700 #else
8701                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8702 #endif
8703                 quote_fmt_scan_string(tmpl->cc);
8704                 quote_fmt_parse();
8705
8706                 buf = quote_fmt_get_buffer();
8707                 if (buf == NULL) {
8708                         alertpanel_error(_("Template Cc format error."));
8709                 } else {
8710                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8711                 }
8712         }
8713
8714         if (tmpl->bcc && *tmpl->bcc != '\0') {
8715 #ifdef USE_ENCHANT
8716                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8717                                 compose->gtkaspell);
8718 #else
8719                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8720 #endif
8721                 quote_fmt_scan_string(tmpl->bcc);
8722                 quote_fmt_parse();
8723
8724                 buf = quote_fmt_get_buffer();
8725                 if (buf == NULL) {
8726                         alertpanel_error(_("Template Bcc format error."));
8727                 } else {
8728                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8729                 }
8730         }
8731
8732         if (tmpl->replyto && *tmpl->replyto != '\0') {
8733 #ifdef USE_ENCHANT
8734                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8735                                 compose->gtkaspell);
8736 #else
8737                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8738 #endif
8739                 quote_fmt_scan_string(tmpl->replyto);
8740                 quote_fmt_parse();
8741
8742                 buf = quote_fmt_get_buffer();
8743                 if (buf == NULL) {
8744                         alertpanel_error(_("Template Reply-To format error."));
8745                 } else {
8746                         compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
8747                 }
8748         }
8749
8750         /* process the subject */
8751         if (tmpl->subject && *tmpl->subject != '\0') {
8752 #ifdef USE_ENCHANT
8753                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8754                                 compose->gtkaspell);
8755 #else
8756                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8757 #endif
8758                 quote_fmt_scan_string(tmpl->subject);
8759                 quote_fmt_parse();
8760
8761                 buf = quote_fmt_get_buffer();
8762                 if (buf == NULL) {
8763                         alertpanel_error(_("Template subject format error."));
8764                 } else {
8765                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8766                 }
8767         }
8768
8769         procmsg_msginfo_free( &dummyinfo );
8770 }
8771
8772 static void compose_destroy(Compose *compose)
8773 {
8774         GtkAllocation allocation;
8775         GtkTextBuffer *buffer;
8776         GtkClipboard *clipboard;
8777
8778         compose_list = g_list_remove(compose_list, compose);
8779
8780         if (compose->updating) {
8781                 debug_print("danger, not destroying anything now\n");
8782                 compose->deferred_destroy = TRUE;
8783                 return;
8784         }
8785
8786         /* NOTE: address_completion_end() does nothing with the window
8787          * however this may change. */
8788         address_completion_end(compose->window);
8789
8790         slist_free_strings_full(compose->to_list);
8791         slist_free_strings_full(compose->newsgroup_list);
8792         slist_free_strings_full(compose->header_list);
8793
8794         slist_free_strings_full(extra_headers);
8795         extra_headers = NULL;
8796
8797         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8798
8799         g_hash_table_destroy(compose->email_hashtable);
8800
8801         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8802                         compose->folder_update_callback_id);
8803
8804         procmsg_msginfo_free(&(compose->targetinfo));
8805         procmsg_msginfo_free(&(compose->replyinfo));
8806         procmsg_msginfo_free(&(compose->fwdinfo));
8807
8808         g_free(compose->replyto);
8809         g_free(compose->cc);
8810         g_free(compose->bcc);
8811         g_free(compose->newsgroups);
8812         g_free(compose->followup_to);
8813
8814         g_free(compose->ml_post);
8815
8816         g_free(compose->inreplyto);
8817         g_free(compose->references);
8818         g_free(compose->msgid);
8819         g_free(compose->boundary);
8820
8821         g_free(compose->redirect_filename);
8822         if (compose->undostruct)
8823                 undo_destroy(compose->undostruct);
8824
8825         g_free(compose->sig_str);
8826
8827         g_free(compose->exteditor_file);
8828
8829         g_free(compose->orig_charset);
8830
8831         g_free(compose->privacy_system);
8832         g_free(compose->encdata);
8833
8834 #ifndef USE_ALT_ADDRBOOK
8835         if (addressbook_get_target_compose() == compose)
8836                 addressbook_set_target_compose(NULL);
8837 #endif
8838 #if USE_ENCHANT
8839         if (compose->gtkaspell) {
8840                 gtkaspell_delete(compose->gtkaspell);
8841                 compose->gtkaspell = NULL;
8842         }
8843 #endif
8844
8845         if (!compose->batch) {
8846                 gtk_widget_get_allocation(compose->window, &allocation);
8847                 prefs_common.compose_width = allocation.width;
8848                 prefs_common.compose_height = allocation.height;
8849         }
8850
8851         if (!gtk_widget_get_parent(compose->paned))
8852                 gtk_widget_destroy(compose->paned);
8853         gtk_widget_destroy(compose->popupmenu);
8854
8855         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8856         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8857         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8858
8859         gtk_widget_destroy(compose->window);
8860         toolbar_destroy(compose->toolbar);
8861         g_free(compose->toolbar);
8862         cm_mutex_free(compose->mutex);
8863         g_free(compose);
8864 }
8865
8866 static void compose_attach_info_free(AttachInfo *ainfo)
8867 {
8868         g_free(ainfo->file);
8869         g_free(ainfo->content_type);
8870         g_free(ainfo->name);
8871         g_free(ainfo->charset);
8872         g_free(ainfo);
8873 }
8874
8875 static void compose_attach_update_label(Compose *compose)
8876 {
8877         GtkTreeIter iter;
8878         gint i = 1;
8879         gchar *text;
8880         GtkTreeModel *model;
8881         
8882         if(compose == NULL)
8883                 return;
8884                 
8885         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8886         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8887                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8888                 return;
8889         }
8890         
8891         while(gtk_tree_model_iter_next(model, &iter))
8892                 i++;
8893         
8894         text = g_strdup_printf("(%d)", i);
8895         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8896         g_free(text);
8897 }
8898
8899 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8900 {
8901         Compose *compose = (Compose *)data;
8902         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8903         GtkTreeSelection *selection;
8904         GList *sel, *cur;
8905         GtkTreeModel *model;
8906
8907         selection = gtk_tree_view_get_selection(tree_view);
8908         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8909
8910         if (!sel) 
8911                 return;
8912
8913         for (cur = sel; cur != NULL; cur = cur->next) {
8914                 GtkTreePath *path = cur->data;
8915                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8916                                                 (model, cur->data);
8917                 cur->data = ref;
8918                 gtk_tree_path_free(path);
8919         }
8920
8921         for (cur = sel; cur != NULL; cur = cur->next) {
8922                 GtkTreeRowReference *ref = cur->data;
8923                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8924                 GtkTreeIter iter;
8925
8926                 if (gtk_tree_model_get_iter(model, &iter, path))
8927                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8928                 
8929                 gtk_tree_path_free(path);
8930                 gtk_tree_row_reference_free(ref);
8931         }
8932
8933         g_list_free(sel);
8934         compose_attach_update_label(compose);
8935 }
8936
8937 static struct _AttachProperty
8938 {
8939         GtkWidget *window;
8940         GtkWidget *mimetype_entry;
8941         GtkWidget *encoding_optmenu;
8942         GtkWidget *path_entry;
8943         GtkWidget *filename_entry;
8944         GtkWidget *ok_btn;
8945         GtkWidget *cancel_btn;
8946 } attach_prop;
8947
8948 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8949 {       
8950         gtk_tree_path_free((GtkTreePath *)ptr);
8951 }
8952
8953 static void compose_attach_property(GtkAction *action, gpointer data)
8954 {
8955         Compose *compose = (Compose *)data;
8956         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8957         AttachInfo *ainfo;
8958         GtkComboBox *optmenu;
8959         GtkTreeSelection *selection;
8960         GList *sel;
8961         GtkTreeModel *model;
8962         GtkTreeIter iter;
8963         GtkTreePath *path;
8964         static gboolean cancelled;
8965
8966         /* only if one selected */
8967         selection = gtk_tree_view_get_selection(tree_view);
8968         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8969                 return;
8970
8971         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8972         if (!sel)
8973                 return;
8974
8975         path = (GtkTreePath *) sel->data;
8976         gtk_tree_model_get_iter(model, &iter, path);
8977         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8978         
8979         if (!ainfo) {
8980                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8981                 g_list_free(sel);
8982                 return;
8983         }               
8984         g_list_free(sel);
8985
8986         if (!attach_prop.window)
8987                 compose_attach_property_create(&cancelled);
8988         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8989         gtk_widget_grab_focus(attach_prop.ok_btn);
8990         gtk_widget_show(attach_prop.window);
8991         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8992                         GTK_WINDOW(compose->window));
8993
8994         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8995         if (ainfo->encoding == ENC_UNKNOWN)
8996                 combobox_select_by_data(optmenu, ENC_BASE64);
8997         else
8998                 combobox_select_by_data(optmenu, ainfo->encoding);
8999
9000         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
9001                            ainfo->content_type ? ainfo->content_type : "");
9002         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
9003                            ainfo->file ? ainfo->file : "");
9004         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
9005                            ainfo->name ? ainfo->name : "");
9006
9007         for (;;) {
9008                 const gchar *entry_text;
9009                 gchar *text;
9010                 gchar *cnttype = NULL;
9011                 gchar *file = NULL;
9012                 off_t size = 0;
9013
9014                 cancelled = FALSE;
9015                 gtk_main();
9016
9017                 gtk_widget_hide(attach_prop.window);
9018                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
9019                 
9020                 if (cancelled)
9021                         break;
9022
9023                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
9024                 if (*entry_text != '\0') {
9025                         gchar *p;
9026
9027                         text = g_strstrip(g_strdup(entry_text));
9028                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
9029                                 cnttype = g_strdup(text);
9030                                 g_free(text);
9031                         } else {
9032                                 alertpanel_error(_("Invalid MIME type."));
9033                                 g_free(text);
9034                                 continue;
9035                         }
9036                 }
9037
9038                 ainfo->encoding = combobox_get_active_data(optmenu);
9039
9040                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
9041                 if (*entry_text != '\0') {
9042                         if (is_file_exist(entry_text) &&
9043                             (size = get_file_size(entry_text)) > 0)
9044                                 file = g_strdup(entry_text);
9045                         else {
9046                                 alertpanel_error
9047                                         (_("File doesn't exist or is empty."));
9048                                 g_free(cnttype);
9049                                 continue;
9050                         }
9051                 }
9052
9053                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
9054                 if (*entry_text != '\0') {
9055                         g_free(ainfo->name);
9056                         ainfo->name = g_strdup(entry_text);
9057                 }
9058
9059                 if (cnttype) {
9060                         g_free(ainfo->content_type);
9061                         ainfo->content_type = cnttype;
9062                 }
9063                 if (file) {
9064                         g_free(ainfo->file);
9065                         ainfo->file = file;
9066                 }
9067                 if (size)
9068                         ainfo->size = (goffset)size;
9069
9070                 /* update tree store */
9071                 text = to_human_readable(ainfo->size);
9072                 gtk_tree_model_get_iter(model, &iter, path);
9073                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
9074                                    COL_MIMETYPE, ainfo->content_type,
9075                                    COL_SIZE, text,
9076                                    COL_NAME, ainfo->name,
9077                                    COL_CHARSET, ainfo->charset,
9078                                    -1);
9079                 
9080                 break;
9081         }
9082
9083         gtk_tree_path_free(path);
9084 }
9085
9086 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9087 { \
9088         label = gtk_label_new(str); \
9089         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9090                          GTK_FILL, 0, 0, 0); \
9091         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9092  \
9093         entry = gtk_entry_new(); \
9094         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9095                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9096 }
9097
9098 static void compose_attach_property_create(gboolean *cancelled)
9099 {
9100         GtkWidget *window;
9101         GtkWidget *vbox;
9102         GtkWidget *table;
9103         GtkWidget *label;
9104         GtkWidget *mimetype_entry;
9105         GtkWidget *hbox;
9106         GtkWidget *optmenu;
9107         GtkListStore *optmenu_menu;
9108         GtkWidget *path_entry;
9109         GtkWidget *filename_entry;
9110         GtkWidget *hbbox;
9111         GtkWidget *ok_btn;
9112         GtkWidget *cancel_btn;
9113         GList     *mime_type_list, *strlist;
9114         GtkTreeIter iter;
9115
9116         debug_print("Creating attach_property window...\n");
9117
9118         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9119         gtk_widget_set_size_request(window, 480, -1);
9120         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9121         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9122         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9123         g_signal_connect(G_OBJECT(window), "delete_event",
9124                          G_CALLBACK(attach_property_delete_event),
9125                          cancelled);
9126         g_signal_connect(G_OBJECT(window), "key_press_event",
9127                          G_CALLBACK(attach_property_key_pressed),
9128                          cancelled);
9129
9130         vbox = gtk_vbox_new(FALSE, 8);
9131         gtk_container_add(GTK_CONTAINER(window), vbox);
9132
9133         table = gtk_table_new(4, 2, FALSE);
9134         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9135         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9136         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9137
9138         label = gtk_label_new(_("MIME type")); 
9139         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9140                          GTK_FILL, 0, 0, 0); 
9141         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9142 #if !GTK_CHECK_VERSION(2, 24, 0)
9143         mimetype_entry = gtk_combo_box_entry_new_text(); 
9144 #else
9145         mimetype_entry = gtk_combo_box_text_new_with_entry();
9146 #endif
9147         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9148                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9149                          
9150         /* stuff with list */
9151         mime_type_list = procmime_get_mime_type_list();
9152         strlist = NULL;
9153         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9154                 MimeType *type = (MimeType *) mime_type_list->data;
9155                 gchar *tmp;
9156
9157                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9158
9159                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9160                         g_free(tmp);
9161                 else
9162                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9163                                         (GCompareFunc)strcmp2);
9164         }
9165
9166         for (mime_type_list = strlist; mime_type_list != NULL; 
9167                 mime_type_list = mime_type_list->next) {
9168 #if !GTK_CHECK_VERSION(2, 24, 0)
9169                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9170 #else
9171                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9172 #endif
9173                 g_free(mime_type_list->data);
9174         }
9175         g_list_free(strlist);
9176         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9177         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9178
9179         label = gtk_label_new(_("Encoding"));
9180         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9181                          GTK_FILL, 0, 0, 0);
9182         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9183
9184         hbox = gtk_hbox_new(FALSE, 0);
9185         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9186                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9187
9188         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9189         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9190
9191         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9192         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9193         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9194         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9195         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9196
9197         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9198
9199         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9200         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9201
9202         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9203                                       &ok_btn, GTK_STOCK_OK,
9204                                       NULL, NULL);
9205         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9206         gtk_widget_grab_default(ok_btn);
9207
9208         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9209                          G_CALLBACK(attach_property_ok),
9210                          cancelled);
9211         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9212                          G_CALLBACK(attach_property_cancel),
9213                          cancelled);
9214
9215         gtk_widget_show_all(vbox);
9216
9217         attach_prop.window           = window;
9218         attach_prop.mimetype_entry   = mimetype_entry;
9219         attach_prop.encoding_optmenu = optmenu;
9220         attach_prop.path_entry       = path_entry;
9221         attach_prop.filename_entry   = filename_entry;
9222         attach_prop.ok_btn           = ok_btn;
9223         attach_prop.cancel_btn       = cancel_btn;
9224 }
9225
9226 #undef SET_LABEL_AND_ENTRY
9227
9228 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9229 {
9230         *cancelled = FALSE;
9231         gtk_main_quit();
9232 }
9233
9234 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9235 {
9236         *cancelled = TRUE;
9237         gtk_main_quit();
9238 }
9239
9240 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9241                                          gboolean *cancelled)
9242 {
9243         *cancelled = TRUE;
9244         gtk_main_quit();
9245
9246         return TRUE;
9247 }
9248
9249 static gboolean attach_property_key_pressed(GtkWidget *widget,
9250                                             GdkEventKey *event,
9251                                             gboolean *cancelled)
9252 {
9253         if (event && event->keyval == GDK_KEY_Escape) {
9254                 *cancelled = TRUE;
9255                 gtk_main_quit();
9256         }
9257         if (event && event->keyval == GDK_KEY_Return) {
9258                 *cancelled = FALSE;
9259                 gtk_main_quit();
9260                 return TRUE;
9261         }
9262         return FALSE;
9263 }
9264
9265 static void compose_exec_ext_editor(Compose *compose)
9266 {
9267 #ifdef G_OS_UNIX
9268         gchar *tmp;
9269         GtkWidget *socket;
9270         GdkNativeWindow socket_wid = 0;
9271         pid_t pid;
9272         gint pipe_fds[2];
9273
9274         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9275                               G_DIR_SEPARATOR, compose);
9276
9277         if (compose_get_ext_editor_uses_socket()) {
9278                 /* Only allow one socket */
9279                 if (compose->exteditor_socket != NULL) {
9280                         if (gtk_widget_is_focus(compose->exteditor_socket)) {
9281                                 /* Move the focus off of the socket */
9282                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9283                         }
9284                         g_free(tmp);
9285                         return;
9286                 }
9287                 /* Create the receiving GtkSocket */
9288                 socket = gtk_socket_new ();
9289                 g_signal_connect (GTK_OBJECT(socket), "plug-removed",
9290                                   G_CALLBACK(compose_ext_editor_plug_removed_cb),
9291                                   compose);
9292                 gtk_box_pack_start(GTK_BOX(compose->edit_vbox), socket, TRUE, TRUE, 0);
9293                 gtk_widget_set_size_request(socket, prefs_common.compose_width, -1);
9294                 /* Realize the socket so that we can use its ID */
9295                 gtk_widget_realize(socket);
9296                 socket_wid = gtk_socket_get_id(GTK_SOCKET (socket));
9297                 compose->exteditor_socket = socket;
9298         }
9299
9300         if (pipe(pipe_fds) < 0) {
9301                 perror("pipe");
9302                 g_free(tmp);
9303                 return;
9304         }
9305
9306         if ((pid = fork()) < 0) {
9307                 perror("fork");
9308                 g_free(tmp);
9309                 return;
9310         }
9311
9312         if (pid != 0) {
9313                 /* close the write side of the pipe */
9314                 close(pipe_fds[1]);
9315
9316                 compose->exteditor_file    = g_strdup(tmp);
9317                 compose->exteditor_pid     = pid;
9318
9319                 compose_set_ext_editor_sensitive(compose, FALSE);
9320
9321 #ifndef G_OS_WIN32
9322                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9323 #else
9324                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9325 #endif
9326                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9327                                                         G_IO_IN,
9328                                                         compose_input_cb,
9329                                                         compose);
9330         } else {        /* process-monitoring process */
9331                 pid_t pid_ed;
9332
9333                 if (setpgid(0, 0))
9334                         perror("setpgid");
9335
9336                 /* close the read side of the pipe */
9337                 close(pipe_fds[0]);
9338
9339                 if (compose_write_body_to_file(compose, tmp) < 0) {
9340                         fd_write_all(pipe_fds[1], "2\n", 2);
9341                         _exit(1);
9342                 }
9343
9344                 pid_ed = compose_exec_ext_editor_real(tmp, socket_wid);
9345                 if (pid_ed < 0) {
9346                         fd_write_all(pipe_fds[1], "1\n", 2);
9347                         _exit(1);
9348                 }
9349
9350                 /* wait until editor is terminated */
9351                 waitpid(pid_ed, NULL, 0);
9352
9353                 fd_write_all(pipe_fds[1], "0\n", 2);
9354
9355                 close(pipe_fds[1]);
9356                 _exit(0);
9357         }
9358
9359         g_free(tmp);
9360 #endif /* G_OS_UNIX */
9361 }
9362
9363 #ifdef G_OS_UNIX
9364 static gboolean compose_get_ext_editor_cmd_valid()
9365 {
9366         gboolean has_s = FALSE;
9367         gboolean has_w = FALSE;
9368         const gchar *p = prefs_common_get_ext_editor_cmd();
9369         if (!p)
9370                 return FALSE;
9371         while ((p = strchr(p, '%'))) {
9372                 p++;
9373                 if (*p == 's') {
9374                         if (has_s)
9375                                 return FALSE;
9376                         has_s = TRUE;
9377                 } else if (*p == 'w') {
9378                         if (has_w)
9379                                 return FALSE;
9380                         has_w = TRUE;
9381                 } else {
9382                         return FALSE;
9383                 }
9384         }
9385         return TRUE;
9386 }
9387
9388 static gint compose_exec_ext_editor_real(const gchar *file, GdkNativeWindow socket_wid)
9389 {
9390         gchar buf[1024];
9391         gchar *p, *s;
9392         gchar **cmdline;
9393         pid_t pid;
9394
9395         cm_return_val_if_fail(file != NULL, -1);
9396
9397         if ((pid = fork()) < 0) {
9398                 perror("fork");
9399                 return -1;
9400         }
9401
9402         if (pid != 0) return pid;
9403
9404         /* grandchild process */
9405
9406         if (setpgid(0, getppid()))
9407                 perror("setpgid");
9408
9409         if (compose_get_ext_editor_cmd_valid()) {
9410                 if (compose_get_ext_editor_uses_socket()) {
9411                         p = g_strdup(prefs_common_get_ext_editor_cmd());
9412                         s = strstr(p, "%w");
9413                         s[1] = 'u';
9414                         if (strstr(p, "%s") < s)
9415                                 g_snprintf(buf, sizeof(buf), p, file, socket_wid);
9416                         else
9417                                 g_snprintf(buf, sizeof(buf), p, socket_wid, file);
9418                         g_free(p);
9419                 } else {
9420                         g_snprintf(buf, sizeof(buf),
9421                                    prefs_common_get_ext_editor_cmd(), file);
9422                 }
9423         } else {
9424                 if (prefs_common_get_ext_editor_cmd())
9425                         g_warning("External editor command-line is invalid: '%s'",
9426                                   prefs_common_get_ext_editor_cmd());
9427                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9428         }
9429
9430         cmdline = strsplit_with_quote(buf, " ", 1024);
9431         execvp(cmdline[0], cmdline);
9432
9433         perror("execvp");
9434         g_strfreev(cmdline);
9435
9436         _exit(1);
9437 }
9438
9439 static gboolean compose_ext_editor_kill(Compose *compose)
9440 {
9441         pid_t pgid = compose->exteditor_pid * -1;
9442         gint ret;
9443
9444         ret = kill(pgid, 0);
9445
9446         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9447                 AlertValue val;
9448                 gchar *msg;
9449
9450                 msg = g_strdup_printf
9451                         (_("The external editor is still working.\n"
9452                            "Force terminating the process?\n"
9453                            "process group id: %d"), -pgid);
9454                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9455                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9456                         
9457                 g_free(msg);
9458
9459                 if (val == G_ALERTALTERNATE) {
9460                         g_source_remove(compose->exteditor_tag);
9461                         g_io_channel_shutdown(compose->exteditor_ch,
9462                                               FALSE, NULL);
9463                         g_io_channel_unref(compose->exteditor_ch);
9464
9465                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9466                         waitpid(compose->exteditor_pid, NULL, 0);
9467
9468                         g_warning("Terminated process group id: %d. "
9469                                   "Temporary file: %s", -pgid, compose->exteditor_file);
9470
9471                         compose_set_ext_editor_sensitive(compose, TRUE);
9472
9473                         g_free(compose->exteditor_file);
9474                         compose->exteditor_file    = NULL;
9475                         compose->exteditor_pid     = -1;
9476                         compose->exteditor_ch      = NULL;
9477                         compose->exteditor_tag     = -1;
9478                 } else
9479                         return FALSE;
9480         }
9481
9482         return TRUE;
9483 }
9484
9485 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9486                                  gpointer data)
9487 {
9488         gchar buf[3] = "3";
9489         Compose *compose = (Compose *)data;
9490         gsize bytes_read;
9491
9492         debug_print("Compose: input from monitoring process\n");
9493
9494         if (g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL) != G_IO_STATUS_NORMAL) {
9495                 bytes_read = 0;
9496                 buf[0] = '\0';
9497         }
9498
9499         g_io_channel_shutdown(source, FALSE, NULL);
9500         g_io_channel_unref(source);
9501
9502         waitpid(compose->exteditor_pid, NULL, 0);
9503
9504         if (buf[0] == '0') {            /* success */
9505                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9506                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9507                 GtkTextIter start, end;
9508                 gchar *chars;
9509
9510                 gtk_text_buffer_set_text(buffer, "", -1);
9511                 compose_insert_file(compose, compose->exteditor_file);
9512                 compose_changed_cb(NULL, compose);
9513                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9514
9515                 if (claws_unlink(compose->exteditor_file) < 0)
9516                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9517
9518                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9519                 gtk_text_buffer_get_start_iter(buffer, &start);
9520                 gtk_text_buffer_get_end_iter(buffer, &end);
9521                 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
9522                 if (chars && strlen(chars) > 0)
9523                         compose->modified = TRUE;
9524                 g_free(chars);
9525         } else if (buf[0] == '1') {     /* failed */
9526                 g_warning("Couldn't exec external editor");
9527                 if (claws_unlink(compose->exteditor_file) < 0)
9528                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9529         } else if (buf[0] == '2') {
9530                 g_warning("Couldn't write to file");
9531         } else if (buf[0] == '3') {
9532                 g_warning("Pipe read failed");
9533         }
9534
9535         compose_set_ext_editor_sensitive(compose, TRUE);
9536
9537         g_free(compose->exteditor_file);
9538         compose->exteditor_file    = NULL;
9539         compose->exteditor_pid     = -1;
9540         compose->exteditor_ch      = NULL;
9541         compose->exteditor_tag     = -1;
9542         if (compose->exteditor_socket) {
9543                 gtk_widget_destroy(compose->exteditor_socket);
9544                 compose->exteditor_socket = NULL;
9545         }
9546
9547
9548         return FALSE;
9549 }
9550
9551 static char *ext_editor_menu_entries[] = {
9552         "Menu/Message/Send",
9553         "Menu/Message/SendLater",
9554         "Menu/Message/InsertFile",
9555         "Menu/Message/InsertSig",
9556         "Menu/Message/ReplaceSig",
9557         "Menu/Message/Save",
9558         "Menu/Message/Print",
9559         "Menu/Edit",
9560 #if USE_ENCHANT
9561         "Menu/Spelling",
9562 #endif
9563         "Menu/Tools/ShowRuler",
9564         "Menu/Tools/Actions",
9565         "Menu/Help",
9566         NULL
9567 };
9568
9569 static void compose_set_ext_editor_sensitive(Compose *compose,
9570                                              gboolean sensitive)
9571 {
9572         int i;
9573
9574         for (i = 0; ext_editor_menu_entries[i]; ++i) {
9575                 cm_menu_set_sensitive_full(compose->ui_manager,
9576                         ext_editor_menu_entries[i], sensitive);
9577         }
9578
9579         if (compose_get_ext_editor_uses_socket()) {
9580                 if (sensitive) {
9581                         if (compose->exteditor_socket)
9582                                 gtk_widget_hide(compose->exteditor_socket);
9583                         gtk_widget_show(compose->scrolledwin);
9584                         if (prefs_common.show_ruler)
9585                                 gtk_widget_show(compose->ruler_hbox);
9586                         /* Fix the focus, as it doesn't go anywhere when the
9587                          * socket is hidden or destroyed */
9588                         gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9589                 } else {
9590                         g_assert (compose->exteditor_socket != NULL);
9591                         /* Fix the focus, as it doesn't go anywhere when the
9592                          * edit box is hidden */
9593                         if (gtk_widget_is_focus(compose->text))
9594                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9595                         gtk_widget_hide(compose->scrolledwin);
9596                         gtk_widget_hide(compose->ruler_hbox);
9597                         gtk_widget_show(compose->exteditor_socket);
9598                 }
9599         } else {
9600                 gtk_widget_set_sensitive(compose->text,                   sensitive);
9601         }
9602         if (compose->toolbar->send_btn)
9603                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9604         if (compose->toolbar->sendl_btn)
9605                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9606         if (compose->toolbar->draft_btn)
9607                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9608         if (compose->toolbar->insert_btn)
9609                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9610         if (compose->toolbar->sig_btn)
9611                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9612         if (compose->toolbar->exteditor_btn)
9613                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9614         if (compose->toolbar->linewrap_current_btn)
9615                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9616         if (compose->toolbar->linewrap_all_btn)
9617                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9618 }
9619
9620 static gboolean compose_get_ext_editor_uses_socket()
9621 {
9622         return (prefs_common_get_ext_editor_cmd() &&
9623                 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9624 }
9625
9626 static gboolean compose_ext_editor_plug_removed_cb(GtkSocket *socket, Compose *compose)
9627 {
9628         compose->exteditor_socket = NULL;
9629         /* returning FALSE allows destruction of the socket */
9630         return FALSE;
9631 }
9632 #endif /* G_OS_UNIX */
9633
9634 /**
9635  * compose_undo_state_changed:
9636  *
9637  * Change the sensivity of the menuentries undo and redo
9638  **/
9639 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9640                                        gint redo_state, gpointer data)
9641 {
9642         Compose *compose = (Compose *)data;
9643
9644         switch (undo_state) {
9645         case UNDO_STATE_TRUE:
9646                 if (!undostruct->undo_state) {
9647                         undostruct->undo_state = TRUE;
9648                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9649                 }
9650                 break;
9651         case UNDO_STATE_FALSE:
9652                 if (undostruct->undo_state) {
9653                         undostruct->undo_state = FALSE;
9654                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9655                 }
9656                 break;
9657         case UNDO_STATE_UNCHANGED:
9658                 break;
9659         case UNDO_STATE_REFRESH:
9660                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9661                 break;
9662         default:
9663                 g_warning("Undo state not recognized");
9664                 break;
9665         }
9666
9667         switch (redo_state) {
9668         case UNDO_STATE_TRUE:
9669                 if (!undostruct->redo_state) {
9670                         undostruct->redo_state = TRUE;
9671                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9672                 }
9673                 break;
9674         case UNDO_STATE_FALSE:
9675                 if (undostruct->redo_state) {
9676                         undostruct->redo_state = FALSE;
9677                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9678                 }
9679                 break;
9680         case UNDO_STATE_UNCHANGED:
9681                 break;
9682         case UNDO_STATE_REFRESH:
9683                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9684                 break;
9685         default:
9686                 g_warning("Redo state not recognized");
9687                 break;
9688         }
9689 }
9690
9691 /* callback functions */
9692
9693 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9694                                         GtkAllocation *allocation,
9695                                         GtkPaned *paned)
9696 {
9697         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9698 }
9699
9700 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9701  * includes "non-client" (windows-izm) in calculation, so this calculation
9702  * may not be accurate.
9703  */
9704 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9705                                         GtkAllocation *allocation,
9706                                         GtkSHRuler *shruler)
9707 {
9708         if (prefs_common.show_ruler) {
9709                 gint char_width = 0, char_height = 0;
9710                 gint line_width_in_chars;
9711
9712                 gtkut_get_font_size(GTK_WIDGET(widget),
9713                                     &char_width, &char_height);
9714                 line_width_in_chars =
9715                         (allocation->width - allocation->x) / char_width;
9716
9717                 /* got the maximum */
9718                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9719                                     0.0, line_width_in_chars, 0);
9720         }
9721
9722         return TRUE;
9723 }
9724
9725 typedef struct {
9726         gchar                   *header;
9727         gchar                   *entry;
9728         ComposePrefType         type;
9729         gboolean                entry_marked;
9730 } HeaderEntryState;
9731
9732 static void account_activated(GtkComboBox *optmenu, gpointer data)
9733 {
9734         Compose *compose = (Compose *)data;
9735
9736         PrefsAccount *ac;
9737         gchar *folderidentifier;
9738         gint account_id = 0;
9739         GtkTreeModel *menu;
9740         GtkTreeIter iter;
9741         GSList *list, *saved_list = NULL;
9742         HeaderEntryState *state;
9743         GtkRcStyle *style = NULL;
9744 #if !GTK_CHECK_VERSION(3, 0, 0)
9745         static GdkColor yellow;
9746         static gboolean color_set = FALSE;
9747 #else
9748         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9749 #endif
9750
9751         /* Get ID of active account in the combo box */
9752         menu = gtk_combo_box_get_model(optmenu);
9753         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9754         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9755
9756         ac = account_find_from_id(account_id);
9757         cm_return_if_fail(ac != NULL);
9758
9759         if (ac != compose->account) {
9760                 compose_select_account(compose, ac, FALSE);
9761
9762                 for (list = compose->header_list; list; list = list->next) {
9763                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9764                         
9765                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9766                                 compose_destroy_headerentry(compose, hentry);
9767                                 continue;
9768                         }
9769                         
9770                         state = g_malloc0(sizeof(HeaderEntryState));
9771                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9772                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9773                         state->entry = gtk_editable_get_chars(
9774                                         GTK_EDITABLE(hentry->entry), 0, -1);
9775                         state->type = hentry->type;
9776                                 
9777 #if !GTK_CHECK_VERSION(3, 0, 0)
9778                         if (!color_set) {
9779                                 gdk_color_parse("#f5f6be", &yellow);
9780                                 color_set = gdk_colormap_alloc_color(
9781                                                         gdk_colormap_get_system(),
9782                                                         &yellow, FALSE, TRUE);
9783                         }
9784 #endif
9785                                 
9786                         style = gtk_widget_get_modifier_style(hentry->entry);
9787                         state->entry_marked = gdk_color_equal(&yellow,
9788                                                 &style->base[GTK_STATE_NORMAL]);
9789
9790                         saved_list = g_slist_append(saved_list, state);
9791                         compose_destroy_headerentry(compose, hentry);
9792                 }
9793
9794                 compose->header_last = NULL;
9795                 g_slist_free(compose->header_list);
9796                 compose->header_list = NULL;
9797                 compose->header_nextrow = 1;
9798                 compose_create_header_entry(compose);
9799                 
9800                 if (ac->set_autocc && ac->auto_cc)
9801                         compose_entry_append(compose, ac->auto_cc,
9802                                                 COMPOSE_CC, PREF_ACCOUNT);
9803
9804                 if (ac->set_autobcc && ac->auto_bcc) 
9805                         compose_entry_append(compose, ac->auto_bcc,
9806                                                 COMPOSE_BCC, PREF_ACCOUNT);
9807         
9808                 if (ac->set_autoreplyto && ac->auto_replyto)
9809                         compose_entry_append(compose, ac->auto_replyto,
9810                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9811                 
9812                 for (list = saved_list; list; list = list->next) {
9813                         state = (HeaderEntryState *) list->data;
9814                         
9815                         compose_add_header_entry(compose, state->header,
9816                                                 state->entry, state->type);
9817                         if (state->entry_marked)
9818                                 compose_entry_mark_default_to(compose, state->entry);
9819                         
9820                         g_free(state->header);  
9821                         g_free(state->entry);
9822                         g_free(state);
9823                 }
9824                 g_slist_free(saved_list);
9825                 
9826                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9827                                         (ac->protocol == A_NNTP) ? 
9828                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9829         }
9830
9831         /* Set message save folder */
9832         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9833                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9834         }
9835         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9836                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9837                            
9838         compose_set_save_to(compose, NULL);
9839         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9840                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9841                                   (compose->account, F_OUTBOX));
9842                 compose_set_save_to(compose, folderidentifier);
9843                 g_free(folderidentifier);
9844         }
9845 }
9846
9847 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9848                             GtkTreeViewColumn *column, Compose *compose)
9849 {
9850         compose_attach_property(NULL, compose);
9851 }
9852
9853 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9854                                       gpointer data)
9855 {
9856         Compose *compose = (Compose *)data;
9857         GtkTreeSelection *attach_selection;
9858         gint attach_nr_selected;
9859         GtkTreePath *path;
9860         
9861         if (!event) return FALSE;
9862
9863         if (event->button == 3) {
9864                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9865                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9866
9867                 /* If no rows, or just one row is selected, right-click should
9868                  * open menu relevant to the row being right-clicked on. We
9869                  * achieve that by selecting the clicked row first. If more
9870                  * than one row is selected, we shouldn't modify the selection,
9871                  * as user may want to remove selected rows (attachments). */
9872                 if (attach_nr_selected < 2) {
9873                         gtk_tree_selection_unselect_all(attach_selection);
9874                         attach_nr_selected = 0;
9875                         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
9876                                         event->x, event->y, &path, NULL, NULL, NULL);
9877                         if (path != NULL) {
9878                                 gtk_tree_selection_select_path(attach_selection, path);
9879                                 gtk_tree_path_free(path);
9880                                 attach_nr_selected++;
9881                         }
9882                 }
9883
9884                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9885                 /* Properties menu item makes no sense with more than one row
9886                  * selected, the properties dialog can only edit one attachment. */
9887                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected == 1));
9888                         
9889                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9890                                NULL, NULL, event->button, event->time);
9891                 return TRUE;                           
9892         }
9893
9894         return FALSE;
9895 }
9896
9897 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9898                                    gpointer data)
9899 {
9900         Compose *compose = (Compose *)data;
9901
9902         if (!event) return FALSE;
9903
9904         switch (event->keyval) {
9905         case GDK_KEY_Delete:
9906                 compose_attach_remove_selected(NULL, compose);
9907                 break;
9908         }
9909         return FALSE;
9910 }
9911
9912 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9913 {
9914         toolbar_comp_set_sensitive(compose, allow);
9915         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9916         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9917 #if USE_ENCHANT
9918         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9919 #endif  
9920         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9921         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9922         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9923         
9924         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9925
9926 }
9927
9928 static void compose_send_cb(GtkAction *action, gpointer data)
9929 {
9930         Compose *compose = (Compose *)data;
9931
9932 #ifdef G_OS_UNIX
9933         if (compose->exteditor_tag != -1) {
9934                 debug_print("ignoring send: external editor still open\n");
9935                 return;
9936         }
9937 #endif
9938         if (prefs_common.work_offline && 
9939             !inc_offline_should_override(TRUE,
9940                 _("Claws Mail needs network access in order "
9941                   "to send this email.")))
9942                 return;
9943         
9944         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9945                 g_source_remove(compose->draft_timeout_tag);
9946                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
9947         }
9948
9949         compose_send(compose);
9950 }
9951
9952 static void compose_send_later_cb(GtkAction *action, gpointer data)
9953 {
9954         Compose *compose = (Compose *)data;
9955         gint val;
9956
9957         inc_lock();
9958         compose_allow_user_actions(compose, FALSE);
9959         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9960         compose_allow_user_actions(compose, TRUE);
9961         inc_unlock();
9962
9963         if (!val) {
9964                 compose_close(compose);
9965         } else if (val == -1) {
9966                 alertpanel_error(_("Could not queue message."));
9967         } else if (val == -2) {
9968                 alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno));
9969         } else if (val == -3) {
9970                 if (privacy_peek_error())
9971                 alertpanel_error(_("Could not queue message for sending:\n\n"
9972                                    "Signature failed: %s"), privacy_get_error());
9973         } else if (val == -4) {
9974                 alertpanel_error(_("Could not queue message for sending:\n\n"
9975                                    "Charset conversion failed."));
9976         } else if (val == -5) {
9977                 alertpanel_error(_("Could not queue message for sending:\n\n"
9978                                    "Couldn't get recipient encryption key."));
9979         } else if (val == -6) {
9980                 /* silent error */
9981         }
9982         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9983 }
9984
9985 #define DRAFTED_AT_EXIT "drafted_at_exit"
9986 static void compose_register_draft(MsgInfo *info)
9987 {
9988         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9989                                       DRAFTED_AT_EXIT, NULL);
9990         FILE *fp = g_fopen(filepath, "ab");
9991         
9992         if (fp) {
9993                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9994                                 info->msgnum);
9995                 fclose(fp);
9996         }
9997                 
9998         g_free(filepath);       
9999 }
10000
10001 gboolean compose_draft (gpointer data, guint action) 
10002 {
10003         Compose *compose = (Compose *)data;
10004         FolderItem *draft;
10005         gchar *tmp;
10006         gchar *sheaders;
10007         gint msgnum;
10008         MsgFlags flag = {0, 0};
10009         static gboolean lock = FALSE;
10010         MsgInfo *newmsginfo;
10011         FILE *fp;
10012         gboolean target_locked = FALSE;
10013         gboolean err = FALSE;
10014
10015         if (lock) return FALSE;
10016
10017         if (compose->sending)
10018                 return TRUE;
10019
10020         draft = account_get_special_folder(compose->account, F_DRAFT);
10021         cm_return_val_if_fail(draft != NULL, FALSE);
10022         
10023         if (!g_mutex_trylock(compose->mutex)) {
10024                 /* we don't want to lock the mutex once it's available,
10025                  * because as the only other part of compose.c locking
10026                  * it is compose_close - which means once unlocked,
10027                  * the compose struct will be freed */
10028                 debug_print("couldn't lock mutex, probably sending\n");
10029                 return FALSE;
10030         }
10031
10032         lock = TRUE;
10033
10034         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10035                               G_DIR_SEPARATOR, compose);
10036         if ((fp = g_fopen(tmp, "wb")) == NULL) {
10037                 FILE_OP_ERROR(tmp, "fopen");
10038                 goto warn_err;
10039         }
10040
10041         /* chmod for security */
10042         if (change_file_mode_rw(fp, tmp) < 0) {
10043                 FILE_OP_ERROR(tmp, "chmod");
10044                 g_warning("can't change file mode");
10045         }
10046
10047         /* Save draft infos */
10048         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
10049         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
10050
10051         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
10052                 gchar *savefolderid;
10053
10054                 savefolderid = compose_get_save_to(compose);
10055                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
10056                 g_free(savefolderid);
10057         }
10058         if (compose->return_receipt) {
10059                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
10060         }
10061         if (compose->privacy_system) {
10062                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
10063                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
10064                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
10065         }
10066
10067         /* Message-ID of message replying to */
10068         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
10069                 gchar *folderid = NULL;
10070
10071                 if (compose->replyinfo->folder)
10072                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
10073                 if (folderid == NULL)
10074                         folderid = g_strdup("NULL");
10075
10076                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
10077                 g_free(folderid);
10078         }
10079         /* Message-ID of message forwarding to */
10080         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
10081                 gchar *folderid = NULL;
10082
10083                 if (compose->fwdinfo->folder)
10084                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
10085                 if (folderid == NULL)
10086                         folderid = g_strdup("NULL");
10087
10088                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
10089                 g_free(folderid);
10090         }
10091
10092         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
10093         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
10094
10095         sheaders = compose_get_manual_headers_info(compose);
10096         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
10097         g_free(sheaders);
10098
10099         /* end of headers */
10100         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
10101
10102         if (err) {
10103                 fclose(fp);
10104                 goto warn_err;
10105         }
10106
10107         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
10108                 fclose(fp);
10109                 goto warn_err;
10110         }
10111         if (fclose(fp) == EOF) {
10112                 goto warn_err;
10113         }
10114         
10115         flag.perm_flags = MSG_NEW|MSG_UNREAD;
10116         if (compose->targetinfo) {
10117                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
10118                 if (target_locked) 
10119                         flag.perm_flags |= MSG_LOCKED;
10120         }
10121         flag.tmp_flags = MSG_DRAFT;
10122
10123         folder_item_scan(draft);
10124         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
10125                 MsgInfo *tmpinfo = NULL;
10126                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
10127                 if (compose->msgid) {
10128                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
10129                 }
10130                 if (tmpinfo) {
10131                         msgnum = tmpinfo->msgnum;
10132                         procmsg_msginfo_free(&tmpinfo);
10133                         debug_print("got draft msgnum %d from scanning\n", msgnum);
10134                 } else {
10135                         debug_print("didn't get draft msgnum after scanning\n");
10136                 }
10137         } else {
10138                 debug_print("got draft msgnum %d from adding\n", msgnum);
10139         }
10140         if (msgnum < 0) {
10141 warn_err:
10142                 claws_unlink(tmp);
10143                 g_free(tmp);
10144                 if (action != COMPOSE_AUTO_SAVE) {
10145                         if (action != COMPOSE_DRAFT_FOR_EXIT)
10146                                 alertpanel_error(_("Could not save draft."));
10147                         else {
10148                                 AlertValue val;
10149                                 gtkut_window_popup(compose->window);
10150                                 val = alertpanel_full(_("Could not save draft"),
10151                                         _("Could not save draft.\n"
10152                                         "Do you want to cancel exit or discard this email?"),
10153                                           _("_Cancel exit"), _("_Discard email"), NULL,
10154                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
10155                                 if (val == G_ALERTALTERNATE) {
10156                                         lock = FALSE;
10157                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10158                                         compose_close(compose);
10159                                         return TRUE;
10160                                 } else {
10161                                         lock = FALSE;
10162                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10163                                         return FALSE;
10164                                 }
10165                         }
10166                 }
10167                 goto unlock;
10168         }
10169         g_free(tmp);
10170
10171         if (compose->mode == COMPOSE_REEDIT) {
10172                 compose_remove_reedit_target(compose, TRUE);
10173         }
10174
10175         newmsginfo = folder_item_get_msginfo(draft, msgnum);
10176
10177         if (newmsginfo) {
10178                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
10179                 if (target_locked)
10180                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
10181                 else
10182                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
10183                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
10184                         procmsg_msginfo_set_flags(newmsginfo, 0,
10185                                                   MSG_HAS_ATTACHMENT);
10186
10187                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
10188                         compose_register_draft(newmsginfo);
10189                 }
10190                 procmsg_msginfo_free(&newmsginfo);
10191         }
10192         
10193         folder_item_scan(draft);
10194         
10195         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
10196                 lock = FALSE;
10197                 g_mutex_unlock(compose->mutex); /* must be done before closing */
10198                 compose_close(compose);
10199                 return TRUE;
10200         } else {
10201                 GStatBuf s;
10202                 gchar *path;
10203
10204                 path = folder_item_fetch_msg(draft, msgnum);
10205                 if (path == NULL) {
10206                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
10207                         goto unlock;
10208                 }
10209                 if (g_stat(path, &s) < 0) {
10210                         FILE_OP_ERROR(path, "stat");
10211                         g_free(path);
10212                         goto unlock;
10213                 }
10214                 g_free(path);
10215
10216                 procmsg_msginfo_free(&(compose->targetinfo));
10217                 compose->targetinfo = procmsg_msginfo_new();
10218                 compose->targetinfo->msgnum = msgnum;
10219                 compose->targetinfo->size = (goffset)s.st_size;
10220                 compose->targetinfo->mtime = s.st_mtime;
10221                 compose->targetinfo->folder = draft;
10222                 if (target_locked)
10223                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
10224                 compose->mode = COMPOSE_REEDIT;
10225                 
10226                 if (action == COMPOSE_AUTO_SAVE) {
10227                         compose->autosaved_draft = compose->targetinfo;
10228                 }
10229                 compose->modified = FALSE;
10230                 compose_set_title(compose);
10231         }
10232 unlock:
10233         lock = FALSE;
10234         g_mutex_unlock(compose->mutex);
10235         return TRUE;
10236 }
10237
10238 void compose_clear_exit_drafts(void)
10239 {
10240         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10241                                       DRAFTED_AT_EXIT, NULL);
10242         if (is_file_exist(filepath))
10243                 claws_unlink(filepath);
10244         
10245         g_free(filepath);
10246 }
10247
10248 void compose_reopen_exit_drafts(void)
10249 {
10250         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10251                                       DRAFTED_AT_EXIT, NULL);
10252         FILE *fp = g_fopen(filepath, "rb");
10253         gchar buf[1024];
10254         
10255         if (fp) {
10256                 while (fgets(buf, sizeof(buf), fp)) {
10257                         gchar **parts = g_strsplit(buf, "\t", 2);
10258                         const gchar *folder = parts[0];
10259                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10260                         
10261                         if (folder && *folder && msgnum > -1) {
10262                                 FolderItem *item = folder_find_item_from_identifier(folder);
10263                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10264                                 if (info)
10265                                         compose_reedit(info, FALSE);
10266                         }
10267                         g_strfreev(parts);
10268                 }       
10269                 fclose(fp);
10270         }       
10271         g_free(filepath);
10272         compose_clear_exit_drafts();
10273 }
10274
10275 static void compose_save_cb(GtkAction *action, gpointer data)
10276 {
10277         Compose *compose = (Compose *)data;
10278         compose_draft(compose, COMPOSE_KEEP_EDITING);
10279         compose->rmode = COMPOSE_REEDIT;
10280 }
10281
10282 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10283 {
10284         if (compose && file_list) {
10285                 GList *tmp;
10286
10287                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10288                         gchar *file = (gchar *) tmp->data;
10289                         gchar *utf8_filename = conv_filename_to_utf8(file);
10290                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10291                         compose_changed_cb(NULL, compose);
10292                         if (free_data) {
10293                         g_free(file);
10294                                 tmp->data = NULL;
10295                         }
10296                         g_free(utf8_filename);
10297                 }
10298         }
10299 }
10300
10301 static void compose_attach_cb(GtkAction *action, gpointer data)
10302 {
10303         Compose *compose = (Compose *)data;
10304         GList *file_list;
10305
10306         if (compose->redirect_filename != NULL)
10307                 return;
10308
10309         /* Set focus_window properly, in case we were called via popup menu,
10310          * which unsets it (via focus_out_event callback on compose window). */
10311         manage_window_focus_in(compose->window, NULL, NULL);
10312
10313         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10314
10315         if (file_list) {
10316                 compose_attach_from_list(compose, file_list, TRUE);
10317                 g_list_free(file_list);
10318         }
10319 }
10320
10321 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10322 {
10323         Compose *compose = (Compose *)data;
10324         GList *file_list;
10325         gint files_inserted = 0;
10326
10327         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10328
10329         if (file_list) {
10330                 GList *tmp;
10331
10332                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10333                         gchar *file = (gchar *) tmp->data;
10334                         gchar *filedup = g_strdup(file);
10335                         gchar *shortfile = g_path_get_basename(filedup);
10336                         ComposeInsertResult res;
10337                         /* insert the file if the file is short or if the user confirmed that
10338                            he/she wants to insert the large file */
10339                         res = compose_insert_file(compose, file);
10340                         if (res == COMPOSE_INSERT_READ_ERROR) {
10341                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10342                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10343                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10344                                                         "for the current encoding, insertion may be incorrect."),
10345                                                         shortfile);
10346                         } else if (res == COMPOSE_INSERT_SUCCESS)
10347                                 files_inserted++;
10348
10349                         g_free(shortfile);
10350                         g_free(filedup);
10351                         g_free(file);
10352                 }
10353                 g_list_free(file_list);
10354         }
10355
10356 #ifdef USE_ENCHANT      
10357         if (files_inserted > 0 && compose->gtkaspell && 
10358             compose->gtkaspell->check_while_typing)
10359                 gtkaspell_highlight_all(compose->gtkaspell);
10360 #endif
10361 }
10362
10363 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10364 {
10365         Compose *compose = (Compose *)data;
10366
10367         compose_insert_sig(compose, FALSE);
10368 }
10369
10370 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10371 {
10372         Compose *compose = (Compose *)data;
10373
10374         compose_insert_sig(compose, TRUE);
10375 }
10376
10377 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10378                               gpointer data)
10379 {
10380         gint x, y;
10381         Compose *compose = (Compose *)data;
10382
10383         gtkut_widget_get_uposition(widget, &x, &y);
10384         if (!compose->batch) {
10385                 prefs_common.compose_x = x;
10386                 prefs_common.compose_y = y;
10387         }
10388         if (compose->sending || compose->updating)
10389                 return TRUE;
10390         compose_close_cb(NULL, compose);
10391         return TRUE;
10392 }
10393
10394 void compose_close_toolbar(Compose *compose)
10395 {
10396         compose_close_cb(NULL, compose);
10397 }
10398
10399 static gboolean compose_can_autosave(Compose *compose)
10400 {
10401         if (compose->privacy_system && compose->use_encryption)
10402                 return prefs_common.autosave && prefs_common.autosave_encrypted;
10403         else
10404                 return prefs_common.autosave;
10405 }
10406
10407 static void compose_close_cb(GtkAction *action, gpointer data)
10408 {
10409         Compose *compose = (Compose *)data;
10410         AlertValue val;
10411
10412 #ifdef G_OS_UNIX
10413         if (compose->exteditor_tag != -1) {
10414                 if (!compose_ext_editor_kill(compose))
10415                         return;
10416         }
10417 #endif
10418
10419         if (compose->modified) {
10420                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10421                 if (!g_mutex_trylock(compose->mutex)) {
10422                         /* we don't want to lock the mutex once it's available,
10423                          * because as the only other part of compose.c locking
10424                          * it is compose_close - which means once unlocked,
10425                          * the compose struct will be freed */
10426                         debug_print("couldn't lock mutex, probably sending\n");
10427                         return;
10428                 }
10429                 if (!reedit) {
10430                         val = alertpanel(_("Discard message"),
10431                                  _("This message has been modified. Discard it?"),
10432                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10433                 } else {
10434                         val = alertpanel(_("Save changes"),
10435                                  _("This message has been modified. Save the latest changes?"),
10436                                  _("_Don't save"), g_strconcat("+", _("_Save to Drafts"), NULL),
10437                                 GTK_STOCK_CANCEL);
10438                 }
10439                 g_mutex_unlock(compose->mutex);
10440                 switch (val) {
10441                 case G_ALERTDEFAULT:
10442                         if (compose_can_autosave(compose) && !reedit)
10443                                 compose_remove_draft(compose);
10444                         break;
10445                 case G_ALERTALTERNATE:
10446                         compose_draft(data, COMPOSE_QUIT_EDITING);
10447                         return;
10448                 default:
10449                         return;
10450                 }
10451         }
10452
10453         compose_close(compose);
10454 }
10455
10456 static void compose_print_cb(GtkAction *action, gpointer data)
10457 {
10458         Compose *compose = (Compose *) data;
10459
10460         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10461         if (compose->targetinfo)
10462                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10463 }
10464
10465 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10466 {
10467         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10468         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10469         Compose *compose = (Compose *) data;
10470
10471         if (active)
10472                 compose->out_encoding = (CharSet)value;
10473 }
10474
10475 static void compose_address_cb(GtkAction *action, gpointer data)
10476 {
10477         Compose *compose = (Compose *)data;
10478
10479 #ifndef USE_ALT_ADDRBOOK
10480         addressbook_open(compose);
10481 #else
10482         GError* error = NULL;
10483         addressbook_connect_signals(compose);
10484         addressbook_dbus_open(TRUE, &error);
10485         if (error) {
10486                 g_warning("%s", error->message);
10487                 g_error_free(error);
10488         }
10489 #endif
10490 }
10491
10492 static void about_show_cb(GtkAction *action, gpointer data)
10493 {
10494         about_show();
10495 }
10496
10497 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10498 {
10499         Compose *compose = (Compose *)data;
10500         Template *tmpl;
10501         gchar *msg;
10502         AlertValue val;
10503
10504         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10505         cm_return_if_fail(tmpl != NULL);
10506
10507         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10508                               tmpl->name);
10509         val = alertpanel(_("Apply template"), msg,
10510                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10511         g_free(msg);
10512
10513         if (val == G_ALERTDEFAULT)
10514                 compose_template_apply(compose, tmpl, TRUE);
10515         else if (val == G_ALERTALTERNATE)
10516                 compose_template_apply(compose, tmpl, FALSE);
10517 }
10518
10519 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10520 {
10521         Compose *compose = (Compose *)data;
10522
10523 #ifdef G_OS_UNIX
10524         if (compose->exteditor_tag != -1) {
10525                 debug_print("ignoring open external editor: external editor still open\n");
10526                 return;
10527         }
10528 #endif
10529         compose_exec_ext_editor(compose);
10530 }
10531
10532 static void compose_undo_cb(GtkAction *action, gpointer data)
10533 {
10534         Compose *compose = (Compose *)data;
10535         gboolean prev_autowrap = compose->autowrap;
10536
10537         compose->autowrap = FALSE;
10538         undo_undo(compose->undostruct);
10539         compose->autowrap = prev_autowrap;
10540 }
10541
10542 static void compose_redo_cb(GtkAction *action, gpointer data)
10543 {
10544         Compose *compose = (Compose *)data;
10545         gboolean prev_autowrap = compose->autowrap;
10546         
10547         compose->autowrap = FALSE;
10548         undo_redo(compose->undostruct);
10549         compose->autowrap = prev_autowrap;
10550 }
10551
10552 static void entry_cut_clipboard(GtkWidget *entry)
10553 {
10554         if (GTK_IS_EDITABLE(entry))
10555                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10556         else if (GTK_IS_TEXT_VIEW(entry))
10557                 gtk_text_buffer_cut_clipboard(
10558                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10559                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10560                         TRUE);
10561 }
10562
10563 static void entry_copy_clipboard(GtkWidget *entry)
10564 {
10565         if (GTK_IS_EDITABLE(entry))
10566                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10567         else if (GTK_IS_TEXT_VIEW(entry))
10568                 gtk_text_buffer_copy_clipboard(
10569                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10570                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10571 }
10572
10573 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10574                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10575 {
10576         if (GTK_IS_TEXT_VIEW(entry)) {
10577                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10578                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10579                 GtkTextIter start_iter, end_iter;
10580                 gint start, end;
10581                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10582
10583                 if (contents == NULL)
10584                         return;
10585         
10586                 /* we shouldn't delete the selection when middle-click-pasting, or we
10587                  * can't mid-click-paste our own selection */
10588                 if (clip != GDK_SELECTION_PRIMARY) {
10589                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10590                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10591                 }
10592                 
10593                 if (insert_place == NULL) {
10594                         /* if insert_place isn't specified, insert at the cursor.
10595                          * used for Ctrl-V pasting */
10596                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10597                         start = gtk_text_iter_get_offset(&start_iter);
10598                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10599                 } else {
10600                         /* if insert_place is specified, paste here.
10601                          * used for mid-click-pasting */
10602                         start = gtk_text_iter_get_offset(insert_place);
10603                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10604                         if (prefs_common.primary_paste_unselects)
10605                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10606                 }
10607                 
10608                 if (!wrap) {
10609                         /* paste unwrapped: mark the paste so it's not wrapped later */
10610                         end = start + strlen(contents);
10611                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10612                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10613                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10614                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10615                         /* rewrap paragraph now (after a mid-click-paste) */
10616                         mark_start = gtk_text_buffer_get_insert(buffer);
10617                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10618                         gtk_text_iter_backward_char(&start_iter);
10619                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10620                 }
10621         } else if (GTK_IS_EDITABLE(entry))
10622                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10623
10624         compose->modified = TRUE;
10625 }
10626
10627 static void entry_allsel(GtkWidget *entry)
10628 {
10629         if (GTK_IS_EDITABLE(entry))
10630                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10631         else if (GTK_IS_TEXT_VIEW(entry)) {
10632                 GtkTextIter startiter, enditer;
10633                 GtkTextBuffer *textbuf;
10634
10635                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10636                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10637                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10638
10639                 gtk_text_buffer_move_mark_by_name(textbuf, 
10640                         "selection_bound", &startiter);
10641                 gtk_text_buffer_move_mark_by_name(textbuf, 
10642                         "insert", &enditer);
10643         }
10644 }
10645
10646 static void compose_cut_cb(GtkAction *action, gpointer data)
10647 {
10648         Compose *compose = (Compose *)data;
10649         if (compose->focused_editable 
10650 #ifndef GENERIC_UMPC
10651             && gtk_widget_has_focus(compose->focused_editable)
10652 #endif
10653             )
10654                 entry_cut_clipboard(compose->focused_editable);
10655 }
10656
10657 static void compose_copy_cb(GtkAction *action, gpointer data)
10658 {
10659         Compose *compose = (Compose *)data;
10660         if (compose->focused_editable 
10661 #ifndef GENERIC_UMPC
10662             && gtk_widget_has_focus(compose->focused_editable)
10663 #endif
10664             )
10665                 entry_copy_clipboard(compose->focused_editable);
10666 }
10667
10668 static void compose_paste_cb(GtkAction *action, gpointer data)
10669 {
10670         Compose *compose = (Compose *)data;
10671         gint prev_autowrap;
10672         GtkTextBuffer *buffer;
10673         BLOCK_WRAP();
10674         if (compose->focused_editable &&
10675 #ifndef GENERIC_UMPC
10676             gtk_widget_has_focus(compose->focused_editable)
10677 #endif
10678                 )
10679                 entry_paste_clipboard(compose, compose->focused_editable, 
10680                                 prefs_common.linewrap_pastes,
10681                                 GDK_SELECTION_CLIPBOARD, NULL);
10682         UNBLOCK_WRAP();
10683
10684 #ifdef USE_ENCHANT
10685         if (
10686 #ifndef GENERIC_UMPC
10687                 gtk_widget_has_focus(compose->text) &&
10688 #endif
10689             compose->gtkaspell && 
10690             compose->gtkaspell->check_while_typing)
10691                 gtkaspell_highlight_all(compose->gtkaspell);
10692 #endif
10693 }
10694
10695 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10696 {
10697         Compose *compose = (Compose *)data;
10698         gint wrap_quote = prefs_common.linewrap_quote;
10699         if (compose->focused_editable 
10700 #ifndef GENERIC_UMPC
10701             && gtk_widget_has_focus(compose->focused_editable)
10702 #endif
10703             ) {
10704                 /* let text_insert() (called directly or at a later time
10705                  * after the gtk_editable_paste_clipboard) know that 
10706                  * text is to be inserted as a quotation. implemented
10707                  * by using a simple refcount... */
10708                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10709                                                 G_OBJECT(compose->focused_editable),
10710                                                 "paste_as_quotation"));
10711                 g_object_set_data(G_OBJECT(compose->focused_editable),
10712                                     "paste_as_quotation",
10713                                     GINT_TO_POINTER(paste_as_quotation + 1));
10714                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10715                 entry_paste_clipboard(compose, compose->focused_editable, 
10716                                 prefs_common.linewrap_pastes,
10717                                 GDK_SELECTION_CLIPBOARD, NULL);
10718                 prefs_common.linewrap_quote = wrap_quote;
10719         }
10720 }
10721
10722 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10723 {
10724         Compose *compose = (Compose *)data;
10725         gint prev_autowrap;
10726         GtkTextBuffer *buffer;
10727         BLOCK_WRAP();
10728         if (compose->focused_editable 
10729 #ifndef GENERIC_UMPC
10730             && gtk_widget_has_focus(compose->focused_editable)
10731 #endif
10732             )
10733                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10734                         GDK_SELECTION_CLIPBOARD, NULL);
10735         UNBLOCK_WRAP();
10736
10737 #ifdef USE_ENCHANT
10738         if (
10739 #ifndef GENERIC_UMPC
10740                 gtk_widget_has_focus(compose->text) &&
10741 #endif
10742             compose->gtkaspell && 
10743             compose->gtkaspell->check_while_typing)
10744                 gtkaspell_highlight_all(compose->gtkaspell);
10745 #endif
10746 }
10747
10748 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10749 {
10750         Compose *compose = (Compose *)data;
10751         gint prev_autowrap;
10752         GtkTextBuffer *buffer;
10753         BLOCK_WRAP();
10754         if (compose->focused_editable 
10755 #ifndef GENERIC_UMPC
10756             && gtk_widget_has_focus(compose->focused_editable)
10757 #endif
10758             )
10759                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10760                         GDK_SELECTION_CLIPBOARD, NULL);
10761         UNBLOCK_WRAP();
10762
10763 #ifdef USE_ENCHANT
10764         if (
10765 #ifndef GENERIC_UMPC
10766                 gtk_widget_has_focus(compose->text) &&
10767 #endif
10768             compose->gtkaspell &&
10769             compose->gtkaspell->check_while_typing)
10770                 gtkaspell_highlight_all(compose->gtkaspell);
10771 #endif
10772 }
10773
10774 static void compose_allsel_cb(GtkAction *action, gpointer data)
10775 {
10776         Compose *compose = (Compose *)data;
10777         if (compose->focused_editable 
10778 #ifndef GENERIC_UMPC
10779             && gtk_widget_has_focus(compose->focused_editable)
10780 #endif
10781             )
10782                 entry_allsel(compose->focused_editable);
10783 }
10784
10785 static void textview_move_beginning_of_line (GtkTextView *text)
10786 {
10787         GtkTextBuffer *buffer;
10788         GtkTextMark *mark;
10789         GtkTextIter ins;
10790
10791         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10792
10793         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10794         mark = gtk_text_buffer_get_insert(buffer);
10795         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10796         gtk_text_iter_set_line_offset(&ins, 0);
10797         gtk_text_buffer_place_cursor(buffer, &ins);
10798 }
10799
10800 static void textview_move_forward_character (GtkTextView *text)
10801 {
10802         GtkTextBuffer *buffer;
10803         GtkTextMark *mark;
10804         GtkTextIter ins;
10805
10806         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10807
10808         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10809         mark = gtk_text_buffer_get_insert(buffer);
10810         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10811         if (gtk_text_iter_forward_cursor_position(&ins))
10812                 gtk_text_buffer_place_cursor(buffer, &ins);
10813 }
10814
10815 static void textview_move_backward_character (GtkTextView *text)
10816 {
10817         GtkTextBuffer *buffer;
10818         GtkTextMark *mark;
10819         GtkTextIter ins;
10820
10821         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10822
10823         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10824         mark = gtk_text_buffer_get_insert(buffer);
10825         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10826         if (gtk_text_iter_backward_cursor_position(&ins))
10827                 gtk_text_buffer_place_cursor(buffer, &ins);
10828 }
10829
10830 static void textview_move_forward_word (GtkTextView *text)
10831 {
10832         GtkTextBuffer *buffer;
10833         GtkTextMark *mark;
10834         GtkTextIter ins;
10835         gint count;
10836
10837         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10838
10839         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10840         mark = gtk_text_buffer_get_insert(buffer);
10841         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10842         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10843         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10844                 gtk_text_iter_backward_word_start(&ins);
10845                 gtk_text_buffer_place_cursor(buffer, &ins);
10846         }
10847 }
10848
10849 static void textview_move_backward_word (GtkTextView *text)
10850 {
10851         GtkTextBuffer *buffer;
10852         GtkTextMark *mark;
10853         GtkTextIter ins;
10854
10855         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10856
10857         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10858         mark = gtk_text_buffer_get_insert(buffer);
10859         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10860         if (gtk_text_iter_backward_word_starts(&ins, 1))
10861                 gtk_text_buffer_place_cursor(buffer, &ins);
10862 }
10863
10864 static void textview_move_end_of_line (GtkTextView *text)
10865 {
10866         GtkTextBuffer *buffer;
10867         GtkTextMark *mark;
10868         GtkTextIter ins;
10869
10870         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10871
10872         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10873         mark = gtk_text_buffer_get_insert(buffer);
10874         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10875         if (gtk_text_iter_forward_to_line_end(&ins))
10876                 gtk_text_buffer_place_cursor(buffer, &ins);
10877 }
10878
10879 static void textview_move_next_line (GtkTextView *text)
10880 {
10881         GtkTextBuffer *buffer;
10882         GtkTextMark *mark;
10883         GtkTextIter ins;
10884         gint offset;
10885
10886         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10887
10888         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10889         mark = gtk_text_buffer_get_insert(buffer);
10890         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10891         offset = gtk_text_iter_get_line_offset(&ins);
10892         if (gtk_text_iter_forward_line(&ins)) {
10893                 gtk_text_iter_set_line_offset(&ins, offset);
10894                 gtk_text_buffer_place_cursor(buffer, &ins);
10895         }
10896 }
10897
10898 static void textview_move_previous_line (GtkTextView *text)
10899 {
10900         GtkTextBuffer *buffer;
10901         GtkTextMark *mark;
10902         GtkTextIter ins;
10903         gint offset;
10904
10905         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10906
10907         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10908         mark = gtk_text_buffer_get_insert(buffer);
10909         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10910         offset = gtk_text_iter_get_line_offset(&ins);
10911         if (gtk_text_iter_backward_line(&ins)) {
10912                 gtk_text_iter_set_line_offset(&ins, offset);
10913                 gtk_text_buffer_place_cursor(buffer, &ins);
10914         }
10915 }
10916
10917 static void textview_delete_forward_character (GtkTextView *text)
10918 {
10919         GtkTextBuffer *buffer;
10920         GtkTextMark *mark;
10921         GtkTextIter ins, end_iter;
10922
10923         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10924
10925         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10926         mark = gtk_text_buffer_get_insert(buffer);
10927         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10928         end_iter = ins;
10929         if (gtk_text_iter_forward_char(&end_iter)) {
10930                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10931         }
10932 }
10933
10934 static void textview_delete_backward_character (GtkTextView *text)
10935 {
10936         GtkTextBuffer *buffer;
10937         GtkTextMark *mark;
10938         GtkTextIter ins, end_iter;
10939
10940         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10941
10942         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10943         mark = gtk_text_buffer_get_insert(buffer);
10944         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10945         end_iter = ins;
10946         if (gtk_text_iter_backward_char(&end_iter)) {
10947                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10948         }
10949 }
10950
10951 static void textview_delete_forward_word (GtkTextView *text)
10952 {
10953         GtkTextBuffer *buffer;
10954         GtkTextMark *mark;
10955         GtkTextIter ins, end_iter;
10956
10957         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10958
10959         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10960         mark = gtk_text_buffer_get_insert(buffer);
10961         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10962         end_iter = ins;
10963         if (gtk_text_iter_forward_word_end(&end_iter)) {
10964                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10965         }
10966 }
10967
10968 static void textview_delete_backward_word (GtkTextView *text)
10969 {
10970         GtkTextBuffer *buffer;
10971         GtkTextMark *mark;
10972         GtkTextIter ins, end_iter;
10973
10974         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10975
10976         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10977         mark = gtk_text_buffer_get_insert(buffer);
10978         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10979         end_iter = ins;
10980         if (gtk_text_iter_backward_word_start(&end_iter)) {
10981                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10982         }
10983 }
10984
10985 static void textview_delete_line (GtkTextView *text)
10986 {
10987         GtkTextBuffer *buffer;
10988         GtkTextMark *mark;
10989         GtkTextIter ins, start_iter, end_iter;
10990
10991         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10992
10993         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10994         mark = gtk_text_buffer_get_insert(buffer);
10995         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10996
10997         start_iter = ins;
10998         gtk_text_iter_set_line_offset(&start_iter, 0);
10999
11000         end_iter = ins;
11001         if (gtk_text_iter_ends_line(&end_iter)){
11002                 if (!gtk_text_iter_forward_char(&end_iter))
11003                         gtk_text_iter_backward_char(&start_iter);
11004         }
11005         else 
11006                 gtk_text_iter_forward_to_line_end(&end_iter);
11007         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
11008 }
11009
11010 static void textview_delete_to_line_end (GtkTextView *text)
11011 {
11012         GtkTextBuffer *buffer;
11013         GtkTextMark *mark;
11014         GtkTextIter ins, end_iter;
11015
11016         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11017
11018         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11019         mark = gtk_text_buffer_get_insert(buffer);
11020         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11021         end_iter = ins;
11022         if (gtk_text_iter_ends_line(&end_iter))
11023                 gtk_text_iter_forward_char(&end_iter);
11024         else
11025                 gtk_text_iter_forward_to_line_end(&end_iter);
11026         gtk_text_buffer_delete(buffer, &ins, &end_iter);
11027 }
11028
11029 #define DO_ACTION(name, act) {                                          \
11030         if(!strcmp(name, a_name)) {                                     \
11031                 return act;                                             \
11032         }                                                               \
11033 }
11034 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
11035 {
11036         const gchar *a_name = gtk_action_get_name(action);
11037         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
11038         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
11039         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
11040         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
11041         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
11042         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
11043         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
11044         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
11045         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
11046         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
11047         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
11048         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
11049         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
11050         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
11051         return -1;
11052 }
11053
11054 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
11055 {
11056         Compose *compose = (Compose *)data;
11057         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11058         ComposeCallAdvancedAction action = -1;
11059         
11060         action = compose_call_advanced_action_from_path(gaction);
11061
11062         static struct {
11063                 void (*do_action) (GtkTextView *text);
11064         } action_table[] = {
11065                 {textview_move_beginning_of_line},
11066                 {textview_move_forward_character},
11067                 {textview_move_backward_character},
11068                 {textview_move_forward_word},
11069                 {textview_move_backward_word},
11070                 {textview_move_end_of_line},
11071                 {textview_move_next_line},
11072                 {textview_move_previous_line},
11073                 {textview_delete_forward_character},
11074                 {textview_delete_backward_character},
11075                 {textview_delete_forward_word},
11076                 {textview_delete_backward_word},
11077                 {textview_delete_line},
11078                 {textview_delete_to_line_end}
11079         };
11080
11081         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
11082
11083         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
11084             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
11085                 if (action_table[action].do_action)
11086                         action_table[action].do_action(text);
11087                 else
11088                         g_warning("Not implemented yet.");
11089         }
11090 }
11091
11092 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
11093 {
11094         GtkAllocation allocation;
11095         GtkWidget *parent;
11096         gchar *str = NULL;
11097         
11098         if (GTK_IS_EDITABLE(widget)) {
11099                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
11100                 gtk_editable_set_position(GTK_EDITABLE(widget), 
11101                         strlen(str));
11102                 g_free(str);
11103                 if ((parent = gtk_widget_get_parent(widget))
11104                  && (parent = gtk_widget_get_parent(parent))
11105                  && (parent = gtk_widget_get_parent(parent))) {
11106                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
11107                                 gtk_widget_get_allocation(widget, &allocation);
11108                                 gint y = allocation.y;
11109                                 gint height = allocation.height;
11110                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
11111                                         (GTK_SCROLLED_WINDOW(parent));
11112
11113                                 gfloat value = gtk_adjustment_get_value(shown);
11114                                 gfloat upper = gtk_adjustment_get_upper(shown);
11115                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
11116                                 if (y < (int)value) {
11117                                         gtk_adjustment_set_value(shown, y - 1);
11118                                 }
11119                                 if ((y + height) > ((int)value + (int)page_size)) {
11120                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
11121                                                 gtk_adjustment_set_value(shown, 
11122                                                         y + height - (int)page_size - 1);
11123                                         } else {
11124                                                 gtk_adjustment_set_value(shown, 
11125                                                         (int)upper - (int)page_size - 1);
11126                                         }
11127                                 }
11128                         }
11129                 }
11130         }
11131
11132         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
11133                 compose->focused_editable = widget;
11134         
11135 #ifdef GENERIC_UMPC
11136         if (GTK_IS_TEXT_VIEW(widget) 
11137             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
11138                 g_object_ref(compose->notebook);
11139                 g_object_ref(compose->edit_vbox);
11140                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11141                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11142                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
11143                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
11144                 g_object_unref(compose->notebook);
11145                 g_object_unref(compose->edit_vbox);
11146                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11147                                         G_CALLBACK(compose_grab_focus_cb),
11148                                         compose);
11149                 gtk_widget_grab_focus(widget);
11150                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11151                                         G_CALLBACK(compose_grab_focus_cb),
11152                                         compose);
11153         } else if (!GTK_IS_TEXT_VIEW(widget) 
11154                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
11155                 g_object_ref(compose->notebook);
11156                 g_object_ref(compose->edit_vbox);
11157                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11158                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11159                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
11160                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
11161                 g_object_unref(compose->notebook);
11162                 g_object_unref(compose->edit_vbox);
11163                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11164                                         G_CALLBACK(compose_grab_focus_cb),
11165                                         compose);
11166                 gtk_widget_grab_focus(widget);
11167                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11168                                         G_CALLBACK(compose_grab_focus_cb),
11169                                         compose);
11170         }
11171 #endif
11172 }
11173
11174 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
11175 {
11176         compose->modified = TRUE;
11177 //      compose_beautify_paragraph(compose, NULL, TRUE);
11178 #ifndef GENERIC_UMPC
11179         compose_set_title(compose);
11180 #endif
11181 }
11182
11183 static void compose_wrap_cb(GtkAction *action, gpointer data)
11184 {
11185         Compose *compose = (Compose *)data;
11186         compose_beautify_paragraph(compose, NULL, TRUE);
11187 }
11188
11189 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
11190 {
11191         Compose *compose = (Compose *)data;
11192         compose_wrap_all_full(compose, TRUE);
11193 }
11194
11195 static void compose_find_cb(GtkAction *action, gpointer data)
11196 {
11197         Compose *compose = (Compose *)data;
11198
11199         message_search_compose(compose);
11200 }
11201
11202 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
11203                                          gpointer        data)
11204 {
11205         Compose *compose = (Compose *)data;
11206         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11207         if (compose->autowrap)
11208                 compose_wrap_all_full(compose, TRUE);
11209         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11210 }
11211
11212 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
11213                                          gpointer        data)
11214 {
11215         Compose *compose = (Compose *)data;
11216         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11217 }
11218
11219 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
11220 {
11221         Compose *compose = (Compose *)data;
11222
11223         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11224 }
11225
11226 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
11227 {
11228         Compose *compose = (Compose *)data;
11229
11230         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11231 }
11232
11233 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
11234 {
11235         g_free(compose->privacy_system);
11236         g_free(compose->encdata);
11237
11238         compose->privacy_system = g_strdup(account->default_privacy_system);
11239         compose_update_privacy_system_menu_item(compose, warn);
11240 }
11241
11242 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
11243 {
11244         Compose *compose = (Compose *)data;
11245
11246         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
11247                 gtk_widget_show(compose->ruler_hbox);
11248                 prefs_common.show_ruler = TRUE;
11249         } else {
11250                 gtk_widget_hide(compose->ruler_hbox);
11251                 gtk_widget_queue_resize(compose->edit_vbox);
11252                 prefs_common.show_ruler = FALSE;
11253         }
11254 }
11255
11256 static void compose_attach_drag_received_cb (GtkWidget          *widget,
11257                                              GdkDragContext     *context,
11258                                              gint                x,
11259                                              gint                y,
11260                                              GtkSelectionData   *data,
11261                                              guint               info,
11262                                              guint               time,
11263                                              gpointer            user_data)
11264 {
11265         Compose *compose = (Compose *)user_data;
11266         GList *list, *tmp;
11267         GdkAtom type;
11268
11269         type = gtk_selection_data_get_data_type(data);
11270         if ((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11271            && gtk_drag_get_source_widget(context) !=
11272                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11273                 list = uri_list_extract_filenames(
11274                         (const gchar *)gtk_selection_data_get_data(data));
11275                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11276                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11277                         compose_attach_append
11278                                 (compose, (const gchar *)tmp->data,
11279                                  utf8_filename, NULL, NULL);
11280                         g_free(utf8_filename);
11281                 }
11282                 if (list) compose_changed_cb(NULL, compose);
11283                 list_free_strings(list);
11284                 g_list_free(list);
11285         } else if (gtk_drag_get_source_widget(context) 
11286                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11287                 /* comes from our summaryview */
11288                 SummaryView * summaryview = NULL;
11289                 GSList * list = NULL, *cur = NULL;
11290                 
11291                 if (mainwindow_get_mainwindow())
11292                         summaryview = mainwindow_get_mainwindow()->summaryview;
11293                 
11294                 if (summaryview)
11295                         list = summary_get_selected_msg_list(summaryview);
11296                 
11297                 for (cur = list; cur; cur = cur->next) {
11298                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11299                         gchar *file = NULL;
11300                         if (msginfo)
11301                                 file = procmsg_get_message_file_full(msginfo, 
11302                                         TRUE, TRUE);
11303                         if (file) {
11304                                 compose_attach_append(compose, (const gchar *)file, 
11305                                         (const gchar *)file, "message/rfc822", NULL);
11306                                 g_free(file);
11307                         }
11308                 }
11309                 g_slist_free(list);
11310         }
11311 }
11312
11313 static gboolean compose_drag_drop(GtkWidget *widget,
11314                                   GdkDragContext *drag_context,
11315                                   gint x, gint y,
11316                                   guint time, gpointer user_data)
11317 {
11318         /* not handling this signal makes compose_insert_drag_received_cb
11319          * called twice */
11320         return TRUE;                                     
11321 }
11322
11323 static gboolean completion_set_focus_to_subject
11324                                         (GtkWidget    *widget,
11325                                          GdkEventKey  *event,
11326                                          Compose      *compose)
11327 {
11328         cm_return_val_if_fail(compose != NULL, FALSE);
11329
11330         /* make backtab move to subject field */
11331         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11332                 gtk_widget_grab_focus(compose->subject_entry);
11333                 return TRUE;
11334         }
11335         return FALSE;
11336 }
11337
11338 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11339                                              GdkDragContext     *drag_context,
11340                                              gint                x,
11341                                              gint                y,
11342                                              GtkSelectionData   *data,
11343                                              guint               info,
11344                                              guint               time,
11345                                              gpointer            user_data)
11346 {
11347         Compose *compose = (Compose *)user_data;
11348         GList *list, *tmp;
11349         GdkAtom type;
11350
11351         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11352          * does not work */
11353         type = gtk_selection_data_get_data_type(data);
11354         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11355                 AlertValue val = G_ALERTDEFAULT;
11356                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11357
11358                 list = uri_list_extract_filenames(ddata);
11359                 if (list == NULL && strstr(ddata, "://")) {
11360                         /* Assume a list of no files, and data has ://, is a remote link */
11361                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11362                         gchar *tmpfile = get_tmp_file();
11363                         str_write_to_file(tmpdata, tmpfile);
11364                         g_free(tmpdata);  
11365                         compose_insert_file(compose, tmpfile);
11366                         claws_unlink(tmpfile);
11367                         g_free(tmpfile);
11368                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11369                         compose_beautify_paragraph(compose, NULL, TRUE);
11370                         return;
11371                 }
11372                 switch (prefs_common.compose_dnd_mode) {
11373                         case COMPOSE_DND_ASK:
11374                                 val = alertpanel_full(_("Insert or attach?"),
11375                                          _("Do you want to insert the contents of the file(s) "
11376                                            "into the message body, or attach it to the email?"),
11377                                           GTK_STOCK_CANCEL, g_strconcat("+", _("_Insert"), NULL), _("_Attach"),
11378                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11379                                 break;
11380                         case COMPOSE_DND_INSERT:
11381                                 val = G_ALERTALTERNATE;
11382                                 break;
11383                         case COMPOSE_DND_ATTACH:
11384                                 val = G_ALERTOTHER;
11385                                 break;
11386                         default:
11387                                 /* unexpected case */
11388                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11389                 }
11390
11391                 if (val & G_ALERTDISABLE) {
11392                         val &= ~G_ALERTDISABLE;
11393                         /* remember what action to perform by default, only if we don't click Cancel */
11394                         if (val == G_ALERTALTERNATE)
11395                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11396                         else if (val == G_ALERTOTHER)
11397                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11398                 }
11399
11400                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11401                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11402                         list_free_strings(list);
11403                         g_list_free(list);
11404                         return;
11405                 } else if (val == G_ALERTOTHER) {
11406                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11407                         list_free_strings(list);
11408                         g_list_free(list);
11409                         return;
11410                 } 
11411
11412                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11413                         compose_insert_file(compose, (const gchar *)tmp->data);
11414                 }
11415                 list_free_strings(list);
11416                 g_list_free(list);
11417                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11418                 return;
11419         }
11420 }
11421
11422 static void compose_header_drag_received_cb (GtkWidget          *widget,
11423                                              GdkDragContext     *drag_context,
11424                                              gint                x,
11425                                              gint                y,
11426                                              GtkSelectionData   *data,
11427                                              guint               info,
11428                                              guint               time,
11429                                              gpointer            user_data)
11430 {
11431         GtkEditable *entry = (GtkEditable *)user_data;
11432         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11433
11434         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11435          * does not work */
11436
11437         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11438                 gchar *decoded=g_new(gchar, strlen(email));
11439                 int start = 0;
11440
11441                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11442                 gtk_editable_delete_text(entry, 0, -1);
11443                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11444                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11445                 g_free(decoded);
11446                 return;
11447         }
11448         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11449 }
11450
11451 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11452 {
11453         Compose *compose = (Compose *)data;
11454
11455         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11456                 compose->return_receipt = TRUE;
11457         else
11458                 compose->return_receipt = FALSE;
11459 }
11460
11461 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11462 {
11463         Compose *compose = (Compose *)data;
11464
11465         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11466                 compose->remove_references = TRUE;
11467         else
11468                 compose->remove_references = FALSE;
11469 }
11470
11471 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11472                                         ComposeHeaderEntry *headerentry)
11473 {
11474         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11475         return FALSE;
11476 }
11477
11478 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11479                                             GdkEventKey *event,
11480                                             ComposeHeaderEntry *headerentry)
11481 {
11482         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11483             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11484             !(event->state & GDK_MODIFIER_MASK) &&
11485             (event->keyval == GDK_KEY_BackSpace) &&
11486             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11487                 gtk_container_remove
11488                         (GTK_CONTAINER(headerentry->compose->header_table),
11489                          headerentry->combo);
11490                 gtk_container_remove
11491                         (GTK_CONTAINER(headerentry->compose->header_table),
11492                          headerentry->entry);
11493                 headerentry->compose->header_list =
11494                         g_slist_remove(headerentry->compose->header_list,
11495                                        headerentry);
11496                 g_free(headerentry);
11497         } else  if (event->keyval == GDK_KEY_Tab) {
11498                 if (headerentry->compose->header_last == headerentry) {
11499                         /* Override default next focus, and give it to subject_entry
11500                          * instead of notebook tabs
11501                          */
11502                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11503                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11504                         return TRUE;
11505                 }
11506         }
11507         return FALSE;
11508 }
11509
11510 static gboolean scroll_postpone(gpointer data)
11511 {
11512         Compose *compose = (Compose *)data;
11513
11514         if (compose->batch)
11515                 return FALSE;
11516
11517         GTK_EVENTS_FLUSH();
11518         compose_show_first_last_header(compose, FALSE);
11519         return FALSE;
11520 }
11521
11522 static void compose_headerentry_changed_cb(GtkWidget *entry,
11523                                     ComposeHeaderEntry *headerentry)
11524 {
11525         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11526                 compose_create_header_entry(headerentry->compose);
11527                 g_signal_handlers_disconnect_matched
11528                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11529                          0, 0, NULL, NULL, headerentry);
11530
11531                 if (!headerentry->compose->batch)
11532                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11533         }
11534 }
11535
11536 static gboolean compose_defer_auto_save_draft(Compose *compose)
11537 {
11538         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11539         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11540         return FALSE;
11541 }
11542
11543 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11544 {
11545         GtkAdjustment *vadj;
11546
11547         cm_return_if_fail(compose);
11548
11549         if(compose->batch)
11550                 return;
11551
11552         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11553         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11554         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11555                                 gtk_widget_get_parent(compose->header_table)));
11556         gtk_adjustment_set_value(vadj, (show_first ?
11557                                 gtk_adjustment_get_lower(vadj) :
11558                                 (gtk_adjustment_get_upper(vadj) -
11559                                 gtk_adjustment_get_page_size(vadj))));
11560         gtk_adjustment_changed(vadj);
11561 }
11562
11563 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11564                           const gchar *text, gint len, Compose *compose)
11565 {
11566         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11567                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11568         GtkTextMark *mark;
11569
11570         cm_return_if_fail(text != NULL);
11571
11572         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11573                                         G_CALLBACK(text_inserted),
11574                                         compose);
11575         if (paste_as_quotation) {
11576                 gchar *new_text;
11577                 const gchar *qmark;
11578                 guint pos = 0;
11579                 GtkTextIter start_iter;
11580
11581                 if (len < 0)
11582                         len = strlen(text);
11583
11584                 new_text = g_strndup(text, len);
11585
11586                 qmark = compose_quote_char_from_context(compose);
11587
11588                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11589                 gtk_text_buffer_place_cursor(buffer, iter);
11590
11591                 pos = gtk_text_iter_get_offset(iter);
11592
11593                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11594                                                   _("Quote format error at line %d."));
11595                 quote_fmt_reset_vartable();
11596                 g_free(new_text);
11597                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11598                                   GINT_TO_POINTER(paste_as_quotation - 1));
11599                                   
11600                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11601                 gtk_text_buffer_place_cursor(buffer, iter);
11602                 gtk_text_buffer_delete_mark(buffer, mark);
11603
11604                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11605                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11606                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11607                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11608                 gtk_text_buffer_delete_mark(buffer, mark);
11609         } else {
11610                 if (strcmp(text, "\n") || compose->automatic_break
11611                 || gtk_text_iter_starts_line(iter)) {
11612                         GtkTextIter before_ins;
11613                         gtk_text_buffer_insert(buffer, iter, text, len);
11614                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11615                                 before_ins = *iter; 
11616                                 gtk_text_iter_backward_chars(&before_ins, len);
11617                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11618                         }
11619                 } else {
11620                         /* check if the preceding is just whitespace or quote */
11621                         GtkTextIter start_line;
11622                         gchar *tmp = NULL, *quote = NULL;
11623                         gint quote_len = 0, is_normal = 0;
11624                         start_line = *iter;
11625                         gtk_text_iter_set_line_offset(&start_line, 0); 
11626                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11627                         g_strstrip(tmp);
11628
11629                         if (*tmp == '\0') {
11630                                 is_normal = 1;
11631                         } else {
11632                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11633                                 if (quote)
11634                                         is_normal = 1;
11635                                 g_free(quote);
11636                         }
11637                         g_free(tmp);
11638                         
11639                         if (is_normal) {
11640                                 gtk_text_buffer_insert(buffer, iter, text, len);
11641                         } else {
11642                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11643                                         iter, text, len, "no_join", NULL);
11644                         }
11645                 }
11646         }
11647         
11648         if (!paste_as_quotation) {
11649                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11650                 compose_beautify_paragraph(compose, iter, FALSE);
11651                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11652                 gtk_text_buffer_delete_mark(buffer, mark);
11653         }
11654
11655         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11656                                           G_CALLBACK(text_inserted),
11657                                           compose);
11658         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11659
11660         if (compose_can_autosave(compose) && 
11661             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11662             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11663                 compose->draft_timeout_tag = g_timeout_add
11664                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11665 }
11666
11667 #if USE_ENCHANT
11668 static void compose_check_all(GtkAction *action, gpointer data)
11669 {
11670         Compose *compose = (Compose *)data;
11671         if (!compose->gtkaspell)
11672                 return;
11673                 
11674         if (gtk_widget_has_focus(compose->subject_entry))
11675                 claws_spell_entry_check_all(
11676                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11677         else
11678                 gtkaspell_check_all(compose->gtkaspell);
11679 }
11680
11681 static void compose_highlight_all(GtkAction *action, gpointer data)
11682 {
11683         Compose *compose = (Compose *)data;
11684         if (compose->gtkaspell) {
11685                 claws_spell_entry_recheck_all(
11686                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11687                 gtkaspell_highlight_all(compose->gtkaspell);
11688         }
11689 }
11690
11691 static void compose_check_backwards(GtkAction *action, gpointer data)
11692 {
11693         Compose *compose = (Compose *)data;
11694         if (!compose->gtkaspell) {
11695                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11696                 return;
11697         }
11698
11699         if (gtk_widget_has_focus(compose->subject_entry))
11700                 claws_spell_entry_check_backwards(
11701                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11702         else
11703                 gtkaspell_check_backwards(compose->gtkaspell);
11704 }
11705
11706 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11707 {
11708         Compose *compose = (Compose *)data;
11709         if (!compose->gtkaspell) {
11710                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11711                 return;
11712         }
11713
11714         if (gtk_widget_has_focus(compose->subject_entry))
11715                 claws_spell_entry_check_forwards_go(
11716                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11717         else
11718                 gtkaspell_check_forwards_go(compose->gtkaspell);
11719 }
11720 #endif
11721
11722 /*!
11723  *\brief        Guess originating forward account from MsgInfo and several 
11724  *              "common preference" settings. Return NULL if no guess. 
11725  */
11726 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11727 {
11728         PrefsAccount *account = NULL;
11729         
11730         cm_return_val_if_fail(msginfo, NULL);
11731         cm_return_val_if_fail(msginfo->folder, NULL);
11732         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11733
11734         if (msginfo->folder->prefs->enable_default_account)
11735                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11736                 
11737         if (!account) 
11738                 account = msginfo->folder->folder->account;
11739                 
11740         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11741                 gchar *to;
11742                 Xstrdup_a(to, msginfo->to, return NULL);
11743                 extract_address(to);
11744                 account = account_find_from_address(to, FALSE);
11745         }
11746
11747         if (!account && prefs_common.forward_account_autosel) {
11748                 gchar cc[BUFFSIZE];
11749                 if (!procheader_get_header_from_msginfo
11750                         (msginfo, cc,sizeof cc , "Cc:")) { 
11751                         gchar *buf = cc + strlen("Cc:");
11752                         extract_address(buf);
11753                         account = account_find_from_address(buf, FALSE);
11754                 }
11755         }
11756         
11757         if (!account && prefs_common.forward_account_autosel) {
11758                 gchar deliveredto[BUFFSIZE];
11759                 if (!procheader_get_header_from_msginfo
11760                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11761                         gchar *buf = deliveredto + strlen("Delivered-To:");
11762                         extract_address(buf);
11763                         account = account_find_from_address(buf, FALSE);
11764                 }
11765         }
11766         
11767         return account;
11768 }
11769
11770 gboolean compose_close(Compose *compose)
11771 {
11772         gint x, y;
11773
11774         cm_return_val_if_fail(compose, FALSE);
11775
11776         if (!g_mutex_trylock(compose->mutex)) {
11777                 /* we have to wait for the (possibly deferred by auto-save)
11778                  * drafting to be done, before destroying the compose under
11779                  * it. */
11780                 debug_print("waiting for drafting to finish...\n");
11781                 compose_allow_user_actions(compose, FALSE);
11782                 if (compose->close_timeout_tag == 0) {
11783                         compose->close_timeout_tag = 
11784                                 g_timeout_add (500, (GSourceFunc) compose_close,
11785                                 compose);
11786                 }
11787                 return TRUE;
11788         }
11789
11790         if (compose->draft_timeout_tag >= 0) {
11791                 g_source_remove(compose->draft_timeout_tag);
11792                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11793         }
11794
11795         gtkut_widget_get_uposition(compose->window, &x, &y);
11796         if (!compose->batch) {
11797                 prefs_common.compose_x = x;
11798                 prefs_common.compose_y = y;
11799         }
11800         g_mutex_unlock(compose->mutex);
11801         compose_destroy(compose);
11802         return FALSE;
11803 }
11804
11805 /**
11806  * Add entry field for each address in list.
11807  * \param compose     E-Mail composition object.
11808  * \param listAddress List of (formatted) E-Mail addresses.
11809  */
11810 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11811         GList *node;
11812         gchar *addr;
11813         node = listAddress;
11814         while( node ) {
11815                 addr = ( gchar * ) node->data;
11816                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11817                 node = g_list_next( node );
11818         }
11819 }
11820
11821 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11822                                     guint action, gboolean opening_multiple)
11823 {
11824         gchar *body = NULL;
11825         GSList *new_msglist = NULL;
11826         MsgInfo *tmp_msginfo = NULL;
11827         gboolean originally_enc = FALSE;
11828         gboolean originally_sig = FALSE;
11829         Compose *compose = NULL;
11830         gchar *s_system = NULL;
11831
11832         cm_return_if_fail(msgview != NULL);
11833
11834         cm_return_if_fail(msginfo_list != NULL);
11835
11836         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11837                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11838                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11839
11840                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11841                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11842                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11843                                                 orig_msginfo, mimeinfo);
11844                         if (tmp_msginfo != NULL) {
11845                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11846
11847                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11848                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11849                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11850
11851                                 tmp_msginfo->folder = orig_msginfo->folder;
11852                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11853                                 if (orig_msginfo->tags) {
11854                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11855                                         tmp_msginfo->folder->tags_dirty = TRUE;
11856                                 }
11857                         }
11858                 }
11859         }
11860
11861         if (!opening_multiple)
11862                 body = messageview_get_selection(msgview);
11863
11864         if (new_msglist) {
11865                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11866                 procmsg_msginfo_free(&tmp_msginfo);
11867                 g_slist_free(new_msglist);
11868         } else
11869                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11870
11871         if (compose && originally_enc) {
11872                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11873         }
11874
11875         if (compose && originally_sig && compose->account->default_sign_reply) {
11876                 compose_force_signing(compose, compose->account, s_system);
11877         }
11878         g_free(s_system);
11879         g_free(body);
11880         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11881 }
11882
11883 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11884                                     guint action)
11885 {
11886         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11887         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11888                 GSList *cur = msginfo_list;
11889                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11890                                                "messages. Opening the windows "
11891                                                "could take some time. Do you "
11892                                                "want to continue?"), 
11893                                                g_slist_length(msginfo_list));
11894                 if (g_slist_length(msginfo_list) > 9
11895                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11896                     != G_ALERTALTERNATE) {
11897                         g_free(msg);
11898                         return;
11899                 }
11900                 g_free(msg);
11901                 /* We'll open multiple compose windows */
11902                 /* let the WM place the next windows */
11903                 compose_force_window_origin = FALSE;
11904                 for (; cur; cur = cur->next) {
11905                         GSList tmplist;
11906                         tmplist.data = cur->data;
11907                         tmplist.next = NULL;
11908                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11909                 }
11910                 compose_force_window_origin = TRUE;
11911         } else {
11912                 /* forwarding multiple mails as attachments is done via a
11913                  * single compose window */
11914                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11915         }
11916 }
11917
11918 void compose_check_for_email_account(Compose *compose)
11919 {
11920         PrefsAccount *ac = NULL, *curr = NULL;
11921         GList *list;
11922         
11923         if (!compose)
11924                 return;
11925
11926         if (compose->account && compose->account->protocol == A_NNTP) {
11927                 ac = account_get_cur_account();
11928                 if (ac->protocol == A_NNTP) {
11929                         list = account_get_list();
11930                         
11931                         for( ; list != NULL ; list = g_list_next(list)) {
11932                                 curr = (PrefsAccount *) list->data;
11933                                 if (curr->protocol != A_NNTP) {
11934                                         ac = curr;
11935                                         break;
11936                                 }
11937                         }
11938                 }
11939                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11940                                         ac->account_id); 
11941         }
11942 }
11943
11944 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11945                                 const gchar *address)
11946 {
11947         GSList *msginfo_list = NULL;
11948         gchar *body =  messageview_get_selection(msgview);
11949         Compose *compose;
11950         
11951         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11952         
11953         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11954         compose_check_for_email_account(compose);
11955         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11956         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11957         compose_reply_set_subject(compose, msginfo);
11958
11959         g_free(body);
11960         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11961 }
11962
11963 void compose_set_position(Compose *compose, gint pos)
11964 {
11965         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11966
11967         gtkut_text_view_set_position(text, pos);
11968 }
11969
11970 gboolean compose_search_string(Compose *compose,
11971                                 const gchar *str, gboolean case_sens)
11972 {
11973         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11974
11975         return gtkut_text_view_search_string(text, str, case_sens);
11976 }
11977
11978 gboolean compose_search_string_backward(Compose *compose,
11979                                 const gchar *str, gboolean case_sens)
11980 {
11981         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11982
11983         return gtkut_text_view_search_string_backward(text, str, case_sens);
11984 }
11985
11986 /* allocate a msginfo structure and populate its data from a compose data structure */
11987 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11988 {
11989         MsgInfo *newmsginfo;
11990         GSList *list;
11991         gchar buf[BUFFSIZE];
11992
11993         cm_return_val_if_fail( compose != NULL, NULL );
11994
11995         newmsginfo = procmsg_msginfo_new();
11996
11997         /* date is now */
11998         get_rfc822_date(buf, sizeof(buf));
11999         newmsginfo->date = g_strdup(buf);
12000
12001         /* from */
12002         if (compose->from_name) {
12003                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
12004                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
12005         }
12006
12007         /* subject */
12008         if (compose->subject_entry)
12009                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
12010
12011         /* to, cc, reply-to, newsgroups */
12012         for (list = compose->header_list; list; list = list->next) {
12013                 gchar *header = gtk_editable_get_chars(
12014                                                                 GTK_EDITABLE(
12015                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
12016                 gchar *entry = gtk_editable_get_chars(
12017                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
12018
12019                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
12020                         if ( newmsginfo->to == NULL ) {
12021                                 newmsginfo->to = g_strdup(entry);
12022                         } else if (entry && *entry) {
12023                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
12024                                 g_free(newmsginfo->to);
12025                                 newmsginfo->to = tmp;
12026                         }
12027                 } else
12028                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
12029                         if ( newmsginfo->cc == NULL ) {
12030                                 newmsginfo->cc = g_strdup(entry);
12031                         } else if (entry && *entry) {
12032                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
12033                                 g_free(newmsginfo->cc);
12034                                 newmsginfo->cc = tmp;
12035                         }
12036                 } else
12037                 if ( strcasecmp(header,
12038                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12039                         if ( newmsginfo->newsgroups == NULL ) {
12040                                 newmsginfo->newsgroups = g_strdup(entry);
12041                         } else if (entry && *entry) {
12042                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
12043                                 g_free(newmsginfo->newsgroups);
12044                                 newmsginfo->newsgroups = tmp;
12045                         }
12046                 }
12047
12048                 g_free(header);
12049                 g_free(entry);  
12050         }
12051
12052         /* other data is unset */
12053
12054         return newmsginfo;
12055 }
12056
12057 #ifdef USE_ENCHANT
12058 /* update compose's dictionaries from folder dict settings */
12059 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
12060                                                 FolderItem *folder_item)
12061 {
12062         cm_return_if_fail(compose != NULL);
12063
12064         if (compose->gtkaspell && folder_item && folder_item->prefs) {
12065                 FolderItemPrefs *prefs = folder_item->prefs;
12066
12067                 if (prefs->enable_default_dictionary)
12068                         gtkaspell_change_dict(compose->gtkaspell,
12069                                         prefs->default_dictionary, FALSE);
12070                 if (folder_item->prefs->enable_default_alt_dictionary)
12071                         gtkaspell_change_alt_dict(compose->gtkaspell,
12072                                         prefs->default_alt_dictionary);
12073                 if (prefs->enable_default_dictionary
12074                         || prefs->enable_default_alt_dictionary)
12075                         compose_spell_menu_changed(compose);
12076         }
12077 }
12078 #endif
12079
12080 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
12081 {
12082         Compose *compose = (Compose *)data;
12083
12084         cm_return_if_fail(compose != NULL);
12085
12086         gtk_widget_grab_focus(compose->text);
12087 }
12088
12089 static void from_name_activate_cb(GtkWidget *widget, gpointer data)
12090 {
12091         gtk_combo_box_popup(GTK_COMBO_BOX(data));
12092 }
12093
12094
12095 /*
12096  * End of Source.
12097  */