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