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