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