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