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