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