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