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