implement enhancement bug 3026, 'replace short by long IDs in dialogue(s) of gpg...
[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;
4806                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4807                 from = g_strdup_printf("%s <%s>",
4808                                        buf, account->address);
4809                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4810         } else {
4811                 from = g_strdup_printf("<%s>",
4812                                        account->address);
4813                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4814         }
4815
4816         g_free(from);
4817
4818         compose_set_title(compose);
4819
4820         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4821                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4822         else
4823                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4824         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4825                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4826         else
4827                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4828                                        
4829         activate_privacy_system(compose, account, FALSE);
4830
4831         if (!init && compose->mode != COMPOSE_REDIRECT) {
4832                 undo_block(compose->undostruct);
4833                 compose_insert_sig(compose, TRUE);
4834                 undo_unblock(compose->undostruct);
4835         }
4836         
4837         header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4838 #if !GTK_CHECK_VERSION(2, 24, 0)
4839         header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4840 #else
4841         if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry->combo), &iter))
4842                 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4843                         header_entry->combo)), &iter, COMBOBOX_TEXT, &header, -1);
4844 #endif
4845         
4846         if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4847                 if (account->protocol == A_NNTP) {
4848                         if (!strcmp(header, _("To:")))
4849                                 combobox_select_by_text(
4850                                         GTK_COMBO_BOX(header_entry->combo),
4851                                         _("Newsgroups:"));
4852                 } else {
4853                         if (!strcmp(header, _("Newsgroups:")))
4854                                 combobox_select_by_text(
4855                                         GTK_COMBO_BOX(header_entry->combo),
4856                                         _("To:"));
4857                 }
4858                 
4859         }
4860         g_free(header);
4861         
4862 #ifdef USE_ENCHANT
4863         /* use account's dict info if set */
4864         if (compose->gtkaspell) {
4865                 if (account->enable_default_dictionary)
4866                         gtkaspell_change_dict(compose->gtkaspell,
4867                                         account->default_dictionary, FALSE);
4868                 if (account->enable_default_alt_dictionary)
4869                         gtkaspell_change_alt_dict(compose->gtkaspell,
4870                                         account->default_alt_dictionary);
4871                 if (account->enable_default_dictionary
4872                         || account->enable_default_alt_dictionary)
4873                         compose_spell_menu_changed(compose);
4874         }
4875 #endif
4876 }
4877
4878 gboolean compose_check_for_valid_recipient(Compose *compose) {
4879         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4880         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4881         gboolean recipient_found = FALSE;
4882         GSList *list;
4883         gchar **strptr;
4884
4885         /* free to and newsgroup list */
4886         slist_free_strings_full(compose->to_list);
4887         compose->to_list = NULL;
4888                         
4889         slist_free_strings_full(compose->newsgroup_list);
4890         compose->newsgroup_list = NULL;
4891
4892         /* search header entries for to and newsgroup entries */
4893         for (list = compose->header_list; list; list = list->next) {
4894                 gchar *header;
4895                 gchar *entry;
4896                 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4897                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4898                 g_strstrip(entry);
4899                 g_strstrip(header);
4900                 if (entry[0] != '\0') {
4901                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4902                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4903                                         compose->to_list = address_list_append(compose->to_list, entry);
4904                                         recipient_found = TRUE;
4905                                 }
4906                         }
4907                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4908                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4909                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4910                                         recipient_found = TRUE;
4911                                 }
4912                         }
4913                 }
4914                 g_free(header);
4915                 g_free(entry);
4916         }
4917         return recipient_found;
4918 }
4919
4920 static gboolean compose_check_for_set_recipients(Compose *compose)
4921 {
4922         if (compose->account->set_autocc && compose->account->auto_cc) {
4923                 gboolean found_other = FALSE;
4924                 GSList *list;
4925                 /* search header entries for to and newsgroup entries */
4926                 for (list = compose->header_list; list; list = list->next) {
4927                         gchar *entry;
4928                         gchar *header;
4929                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4930                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4931                         g_strstrip(entry);
4932                         g_strstrip(header);
4933                         if (strcmp(entry, compose->account->auto_cc)
4934                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4935                                 found_other = TRUE;
4936                                 g_free(entry);
4937                                 break;
4938                         }
4939                         g_free(entry);
4940                         g_free(header);
4941                 }
4942                 if (!found_other) {
4943                         AlertValue aval;
4944                         if (compose->batch) {
4945                                 gtk_widget_show_all(compose->window);
4946                         }
4947                         aval = alertpanel(_("Send"),
4948                                           _("The only recipient is the default CC address. Send anyway?"),
4949                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4950                         if (aval != G_ALERTALTERNATE)
4951                                 return FALSE;
4952                 }
4953         }
4954         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4955                 gboolean found_other = FALSE;
4956                 GSList *list;
4957                 /* search header entries for to and newsgroup entries */
4958                 for (list = compose->header_list; list; list = list->next) {
4959                         gchar *entry;
4960                         gchar *header;
4961                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4962                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4963                         g_strstrip(entry);
4964                         g_strstrip(header);
4965                         if (strcmp(entry, compose->account->auto_bcc)
4966                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4967                                 found_other = TRUE;
4968                                 g_free(entry);
4969                                 break;
4970                         }
4971                         g_free(entry);
4972                         g_free(header);
4973                 }
4974                 if (!found_other) {
4975                         AlertValue aval;
4976                         if (compose->batch) {
4977                                 gtk_widget_show_all(compose->window);
4978                         }
4979                         aval = alertpanel(_("Send"),
4980                                           _("The only recipient is the default BCC address. Send anyway?"),
4981                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4982                         if (aval != G_ALERTALTERNATE)
4983                                 return FALSE;
4984                 }
4985         }
4986         return TRUE;
4987 }
4988
4989 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4990 {
4991         const gchar *str;
4992
4993         if (compose_check_for_valid_recipient(compose) == FALSE) {
4994                 if (compose->batch) {
4995                         gtk_widget_show_all(compose->window);
4996                 }
4997                 alertpanel_error(_("Recipient is not specified."));
4998                 return FALSE;
4999         }
5000
5001         if (compose_check_for_set_recipients(compose) == FALSE) {
5002                 return FALSE;
5003         }
5004
5005         if (!compose->batch && prefs_common.warn_empty_subj == TRUE) {
5006                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5007                 if (*str == '\0' && check_everything == TRUE &&
5008                     compose->mode != COMPOSE_REDIRECT) {
5009                         AlertValue aval;
5010                         gchar *button_label;
5011                         gchar *message;
5012
5013                         if (compose->sending)
5014                                 button_label = _("+_Send");
5015                         else
5016                                 button_label = _("+_Queue");
5017                         message = g_strdup_printf(_("Subject is empty. %s"),
5018                                         compose->sending?_("Send it anyway?"):
5019                                         _("Queue it anyway?"));
5020
5021                         aval = alertpanel_full(compose->sending?_("Send"):_("Send later"), message,
5022                                                GTK_STOCK_CANCEL, button_label, NULL, TRUE, NULL,
5023                                                ALERT_QUESTION, G_ALERTDEFAULT);
5024                         g_free(message);
5025                         if (aval & G_ALERTDISABLE) {
5026                                 aval &= ~G_ALERTDISABLE;
5027                                 prefs_common.warn_empty_subj = FALSE;
5028                         }
5029                         if (aval != G_ALERTALTERNATE)
5030                                 return FALSE;
5031                 }
5032         }
5033
5034         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5035                 return FALSE;
5036
5037         return TRUE;
5038 }
5039
5040 gint compose_send(Compose *compose)
5041 {
5042         gint msgnum;
5043         FolderItem *folder = NULL;
5044         gint val = -1;
5045         gchar *msgpath = NULL;
5046         gboolean discard_window = FALSE;
5047         gchar *errstr = NULL;
5048         gchar *tmsgid = NULL;
5049         MainWindow *mainwin = mainwindow_get_mainwindow();
5050         gboolean queued_removed = FALSE;
5051
5052         if (prefs_common.send_dialog_invisible
5053                         || compose->batch == TRUE)
5054                 discard_window = TRUE;
5055
5056         compose_allow_user_actions (compose, FALSE);
5057         compose->sending = TRUE;
5058
5059         if (compose_check_entries(compose, TRUE) == FALSE) {
5060                 if (compose->batch) {
5061                         gtk_widget_show_all(compose->window);
5062                 }
5063                 goto bail;
5064         }
5065
5066         inc_lock();
5067         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5068
5069         if (val) {
5070                 if (compose->batch) {
5071                         gtk_widget_show_all(compose->window);
5072                 }
5073                 if (val == -4) {
5074                         alertpanel_error(_("Could not queue message for sending:\n\n"
5075                                            "Charset conversion failed."));
5076                 } else if (val == -5) {
5077                         alertpanel_error(_("Could not queue message for sending:\n\n"
5078                                            "Couldn't get recipient encryption key."));
5079                 } else if (val == -6) {
5080                         /* silent error */
5081                 } else if (val == -3) {
5082                         if (privacy_peek_error())
5083                         alertpanel_error(_("Could not queue message for sending:\n\n"
5084                                            "Signature failed: %s"), privacy_get_error());
5085                 } else if (val == -2 && errno != 0) {
5086                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5087                 } else {
5088                         alertpanel_error(_("Could not queue message for sending."));
5089                 }
5090                 goto bail;
5091         }
5092
5093         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5094         if (discard_window) {
5095                 compose->sending = FALSE;
5096                 compose_close(compose);
5097                 /* No more compose access in the normal codepath 
5098                  * after this point! */
5099                 compose = NULL;
5100         }
5101
5102         if (msgnum == 0) {
5103                 alertpanel_error(_("The message was queued but could not be "
5104                                    "sent.\nUse \"Send queued messages\" from "
5105                                    "the main window to retry."));
5106                 if (!discard_window) {
5107                         goto bail;
5108                 }
5109                 inc_unlock();
5110                 g_free(tmsgid);
5111                 return -1;
5112         }
5113         if (msgpath == NULL) {
5114                 msgpath = folder_item_fetch_msg(folder, msgnum);
5115                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5116                 g_free(msgpath);
5117         } else {
5118                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5119                 claws_unlink(msgpath);
5120                 g_free(msgpath);
5121         }
5122         if (!discard_window) {
5123                 if (val != 0) {
5124                         if (!queued_removed)
5125                                 folder_item_remove_msg(folder, msgnum);
5126                         folder_item_scan(folder);
5127                         if (tmsgid) {
5128                                 /* make sure we delete that */
5129                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5130                                 if (tmp) {
5131                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5132                                         folder_item_remove_msg(folder, tmp->msgnum);
5133                                         procmsg_msginfo_free(tmp);
5134                                 } 
5135                         }
5136                 }
5137         }
5138
5139         if (val == 0) {
5140                 if (!queued_removed)
5141                         folder_item_remove_msg(folder, msgnum);
5142                 folder_item_scan(folder);
5143                 if (tmsgid) {
5144                         /* make sure we delete that */
5145                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5146                         if (tmp) {
5147                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5148                                 folder_item_remove_msg(folder, tmp->msgnum);
5149                                 procmsg_msginfo_free(tmp);
5150                         }
5151                 }
5152                 if (!discard_window) {
5153                         compose->sending = FALSE;
5154                         compose_allow_user_actions (compose, TRUE);
5155                         compose_close(compose);
5156                 }
5157         } else {
5158                 if (errstr) {
5159                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5160                                    "the main window to retry."), errstr);
5161                         g_free(errstr);
5162                 } else {
5163                         alertpanel_error_log(_("The message was queued but could not be "
5164                                    "sent.\nUse \"Send queued messages\" from "
5165                                    "the main window to retry."));
5166                 }
5167                 if (!discard_window) {
5168                         goto bail;              
5169                 }
5170                 inc_unlock();
5171                 g_free(tmsgid);
5172                 return -1;
5173         }
5174         g_free(tmsgid);
5175         inc_unlock();
5176         toolbar_main_set_sensitive(mainwin);
5177         main_window_set_menu_sensitive(mainwin);
5178         return 0;
5179
5180 bail:
5181         inc_unlock();
5182         g_free(tmsgid);
5183         compose_allow_user_actions (compose, TRUE);
5184         compose->sending = FALSE;
5185         compose->modified = TRUE; 
5186         toolbar_main_set_sensitive(mainwin);
5187         main_window_set_menu_sensitive(mainwin);
5188
5189         return -1;
5190 }
5191
5192 static gboolean compose_use_attach(Compose *compose) 
5193 {
5194         GtkTreeModel *model = gtk_tree_view_get_model
5195                                 (GTK_TREE_VIEW(compose->attach_clist));
5196         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5197 }
5198
5199 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5200                                                            FILE *fp)
5201 {
5202         gchar buf[BUFFSIZE];
5203         gchar *str;
5204         gboolean first_to_address;
5205         gboolean first_cc_address;
5206         GSList *list;
5207         ComposeHeaderEntry *headerentry;
5208         const gchar *headerentryname;
5209         const gchar *cc_hdr;
5210         const gchar *to_hdr;
5211         gboolean err = FALSE;
5212
5213         debug_print("Writing redirect header\n");
5214
5215         cc_hdr = prefs_common_translated_header_name("Cc:");
5216         to_hdr = prefs_common_translated_header_name("To:");
5217
5218         first_to_address = TRUE;
5219         for (list = compose->header_list; list; list = list->next) {
5220                 headerentry = ((ComposeHeaderEntry *)list->data);
5221                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5222
5223                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5224                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5225                         Xstrdup_a(str, entstr, return -1);
5226                         g_strstrip(str);
5227                         if (str[0] != '\0') {
5228                                 compose_convert_header
5229                                         (compose, buf, sizeof(buf), str,
5230                                         strlen("Resent-To") + 2, TRUE);
5231
5232                                 if (first_to_address) {
5233                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5234                                         first_to_address = FALSE;
5235                                 } else {
5236                                         err |= (fprintf(fp, ",") < 0);
5237                                 }
5238                                 err |= (fprintf(fp, "%s", buf) < 0);
5239                         }
5240                 }
5241         }
5242         if (!first_to_address) {
5243                 err |= (fprintf(fp, "\n") < 0);
5244         }
5245
5246         first_cc_address = TRUE;
5247         for (list = compose->header_list; list; list = list->next) {
5248                 headerentry = ((ComposeHeaderEntry *)list->data);
5249                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5250
5251                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5252                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5253                         Xstrdup_a(str, strg, return -1);
5254                         g_strstrip(str);
5255                         if (str[0] != '\0') {
5256                                 compose_convert_header
5257                                         (compose, buf, sizeof(buf), str,
5258                                         strlen("Resent-Cc") + 2, TRUE);
5259
5260                                 if (first_cc_address) {
5261                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5262                                         first_cc_address = FALSE;
5263                                 } else {
5264                                         err |= (fprintf(fp, ",") < 0);
5265                                 }
5266                                 err |= (fprintf(fp, "%s", buf) < 0);
5267                         }
5268                 }
5269         }
5270         if (!first_cc_address) {
5271                 err |= (fprintf(fp, "\n") < 0);
5272         }
5273         
5274         return (err ? -1:0);
5275 }
5276
5277 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5278 {
5279         gchar buf[BUFFSIZE];
5280         gchar *str;
5281         const gchar *entstr;
5282         /* struct utsname utsbuf; */
5283         gboolean err = FALSE;
5284
5285         cm_return_val_if_fail(fp != NULL, -1);
5286         cm_return_val_if_fail(compose->account != NULL, -1);
5287         cm_return_val_if_fail(compose->account->address != NULL, -1);
5288
5289         /* Resent-Date */
5290         get_rfc822_date(buf, sizeof(buf));
5291         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5292
5293         /* Resent-From */
5294         if (compose->account->name && *compose->account->name) {
5295                 compose_convert_header
5296                         (compose, buf, sizeof(buf), compose->account->name,
5297                          strlen("From: "), TRUE);
5298                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5299                         buf, compose->account->address) < 0);
5300         } else
5301                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5302
5303         /* Subject */
5304         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5305         if (*entstr != '\0') {
5306                 Xstrdup_a(str, entstr, return -1);
5307                 g_strstrip(str);
5308                 if (*str != '\0') {
5309                         compose_convert_header(compose, buf, sizeof(buf), str,
5310                                                strlen("Subject: "), FALSE);
5311                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5312                 }
5313         }
5314
5315         /* Resent-Message-ID */
5316         if (compose->account->set_domain && compose->account->domain) {
5317                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5318         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5319                 g_snprintf(buf, sizeof(buf), "%s", 
5320                         strchr(compose->account->address, '@') ?
5321                                 strchr(compose->account->address, '@')+1 :
5322                                 compose->account->address);
5323         } else {
5324                 g_snprintf(buf, sizeof(buf), "%s", "");
5325         }
5326
5327         if (compose->account->gen_msgid) {
5328                 gchar *addr = NULL;
5329                 if (compose->account->msgid_with_addr) {
5330                         addr = compose->account->address;
5331                 }
5332                 generate_msgid(buf, sizeof(buf), addr);
5333                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5334                 compose->msgid = g_strdup(buf);
5335         } else {
5336                 compose->msgid = NULL;
5337         }
5338
5339         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5340                 return -1;
5341
5342         /* separator between header and body */
5343         err |= (fputs("\n", fp) == EOF);
5344
5345         return (err ? -1:0);
5346 }
5347
5348 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5349 {
5350         FILE *fp;
5351         size_t len;
5352         gchar buf[BUFFSIZE];
5353         int i = 0;
5354         gboolean skip = FALSE;
5355         gboolean err = FALSE;
5356         gchar *not_included[]={
5357                 "Return-Path:",         "Delivered-To:",        "Received:",
5358                 "Subject:",             "X-UIDL:",              "AF:",
5359                 "NF:",                  "PS:",                  "SRH:",
5360                 "SFN:",                 "DSR:",                 "MID:",
5361                 "CFG:",                 "PT:",                  "S:",
5362                 "RQ:",                  "SSV:",                 "NSV:",
5363                 "SSH:",                 "R:",                   "MAID:",
5364                 "NAID:",                "RMID:",                "FMID:",
5365                 "SCF:",                 "RRCPT:",               "NG:",
5366                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5367                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5368                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5369                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5370                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5371                 NULL
5372                 };
5373         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5374                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5375                 return -1;
5376         }
5377
5378         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5379                 skip = FALSE;
5380                 for (i = 0; not_included[i] != NULL; i++) {
5381                         if (g_ascii_strncasecmp(buf, not_included[i],
5382                                                 strlen(not_included[i])) == 0) {
5383                                 skip = TRUE;
5384                                 break;
5385                         }
5386                 }
5387                 if (skip)
5388                         continue;
5389                 if (fputs(buf, fdest) == -1)
5390                         goto error;
5391
5392                 if (!prefs_common.redirect_keep_from) {
5393                         if (g_ascii_strncasecmp(buf, "From:",
5394                                           strlen("From:")) == 0) {
5395                                 err |= (fputs(" (by way of ", fdest) == EOF);
5396                                 if (compose->account->name
5397                                     && *compose->account->name) {
5398                                         compose_convert_header
5399                                                 (compose, buf, sizeof(buf),
5400                                                  compose->account->name,
5401                                                  strlen("From: "),
5402                                                  FALSE);
5403                                         err |= (fprintf(fdest, "%s <%s>",
5404                                                 buf,
5405                                                 compose->account->address) < 0);
5406                                 } else
5407                                         err |= (fprintf(fdest, "%s",
5408                                                 compose->account->address) < 0);
5409                                 err |= (fputs(")", fdest) == EOF);
5410                         }
5411                 }
5412
5413                 if (fputs("\n", fdest) == -1)
5414                         goto error;
5415         }
5416
5417         if (err)
5418                 goto error;
5419
5420         if (compose_redirect_write_headers(compose, fdest))
5421                 goto error;
5422
5423         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5424                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5425                         goto error;
5426         }
5427
5428         fclose(fp);
5429
5430         return 0;
5431 error:
5432         fclose(fp);
5433
5434         return -1;
5435 }
5436
5437 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5438 {
5439         GtkTextBuffer *buffer;
5440         GtkTextIter start, end;
5441         gchar *chars;
5442         gchar *buf;
5443         const gchar *out_codeset;
5444         EncodingType encoding = ENC_UNKNOWN;
5445         MimeInfo *mimemsg, *mimetext;
5446         gint line;
5447         const gchar *src_codeset = CS_INTERNAL;
5448         gchar *from_addr = NULL;
5449         gchar *from_name = NULL;
5450
5451         if (action == COMPOSE_WRITE_FOR_SEND)
5452                 attach_parts = TRUE;
5453
5454         /* create message MimeInfo */
5455         mimemsg = procmime_mimeinfo_new();
5456         mimemsg->type = MIMETYPE_MESSAGE;
5457         mimemsg->subtype = g_strdup("rfc822");
5458         mimemsg->content = MIMECONTENT_MEM;
5459         mimemsg->tmp = TRUE; /* must free content later */
5460         mimemsg->data.mem = compose_get_header(compose);
5461
5462         /* Create text part MimeInfo */
5463         /* get all composed text */
5464         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5465         gtk_text_buffer_get_start_iter(buffer, &start);
5466         gtk_text_buffer_get_end_iter(buffer, &end);
5467         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5468
5469         out_codeset = conv_get_charset_str(compose->out_encoding);
5470
5471         if (!out_codeset && is_ascii_str(chars)) {
5472                 out_codeset = CS_US_ASCII;
5473         } else if (prefs_common.outgoing_fallback_to_ascii &&
5474                    is_ascii_str(chars)) {
5475                 out_codeset = CS_US_ASCII;
5476                 encoding = ENC_7BIT;
5477         }
5478
5479         if (!out_codeset) {
5480                 gchar *test_conv_global_out = NULL;
5481                 gchar *test_conv_reply = NULL;
5482
5483                 /* automatic mode. be automatic. */
5484                 codeconv_set_strict(TRUE);
5485
5486                 out_codeset = conv_get_outgoing_charset_str();
5487                 if (out_codeset) {
5488                         debug_print("trying to convert to %s\n", out_codeset);
5489                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5490                 }
5491
5492                 if (!test_conv_global_out && compose->orig_charset
5493                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5494                         out_codeset = compose->orig_charset;
5495                         debug_print("failure; trying to convert to %s\n", out_codeset);
5496                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5497                 }
5498
5499                 if (!test_conv_global_out && !test_conv_reply) {
5500                         /* we're lost */
5501                         out_codeset = CS_INTERNAL;
5502                         debug_print("failure; finally using %s\n", out_codeset);
5503                 }
5504                 g_free(test_conv_global_out);
5505                 g_free(test_conv_reply);
5506                 codeconv_set_strict(FALSE);
5507         }
5508
5509         if (encoding == ENC_UNKNOWN) {
5510                 if (prefs_common.encoding_method == CTE_BASE64)
5511                         encoding = ENC_BASE64;
5512                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5513                         encoding = ENC_QUOTED_PRINTABLE;
5514                 else if (prefs_common.encoding_method == CTE_8BIT)
5515                         encoding = ENC_8BIT;
5516                 else
5517                         encoding = procmime_get_encoding_for_charset(out_codeset);
5518         }
5519
5520         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5521                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5522
5523         if (action == COMPOSE_WRITE_FOR_SEND) {
5524                 codeconv_set_strict(TRUE);
5525                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5526                 codeconv_set_strict(FALSE);
5527
5528                 if (!buf) {
5529                         AlertValue aval;
5530                         gchar *msg;
5531
5532                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5533                                                 "to the specified %s charset.\n"
5534                                                 "Send it as %s?"), out_codeset, src_codeset);
5535                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5536                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5537                         g_free(msg);
5538
5539                         if (aval != G_ALERTALTERNATE) {
5540                                 g_free(chars);
5541                                 return -3;
5542                         } else {
5543                                 buf = chars;
5544                                 out_codeset = src_codeset;
5545                                 chars = NULL;
5546                         }
5547                 }
5548         } else {
5549                 buf = chars;
5550                 out_codeset = src_codeset;
5551                 chars = NULL;
5552         }
5553         g_free(chars);
5554
5555         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5556                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5557                     strstr(buf, "\nFrom ") != NULL) {
5558                         encoding = ENC_QUOTED_PRINTABLE;
5559                 }
5560         }
5561
5562         mimetext = procmime_mimeinfo_new();
5563         mimetext->content = MIMECONTENT_MEM;
5564         mimetext->tmp = TRUE; /* must free content later */
5565         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5566          * and free the data, which we need later. */
5567         mimetext->data.mem = g_strdup(buf); 
5568         mimetext->type = MIMETYPE_TEXT;
5569         mimetext->subtype = g_strdup("plain");
5570         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5571                             g_strdup(out_codeset));
5572                             
5573         /* protect trailing spaces when signing message */
5574         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5575             privacy_system_can_sign(compose->privacy_system)) {
5576                 encoding = ENC_QUOTED_PRINTABLE;
5577         }
5578         
5579         debug_print("main text: %zd bytes encoded as %s in %d\n",
5580                 strlen(buf), out_codeset, encoding);
5581
5582         /* check for line length limit */
5583         if (action == COMPOSE_WRITE_FOR_SEND &&
5584             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5585             check_line_length(buf, 1000, &line) < 0) {
5586                 AlertValue aval;
5587                 gchar *msg;
5588
5589                 msg = g_strdup_printf
5590                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5591                            "The contents of the message might be broken on the way to the delivery.\n"
5592                            "\n"
5593                            "Send it anyway?"), line + 1);
5594                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5595                 g_free(msg);
5596                 if (aval != G_ALERTALTERNATE) {
5597                         g_free(buf);
5598                         return -1;
5599                 }
5600         }
5601         
5602         if (encoding != ENC_UNKNOWN)
5603                 procmime_encode_content(mimetext, encoding);
5604
5605         /* append attachment parts */
5606         if (compose_use_attach(compose) && attach_parts) {
5607                 MimeInfo *mimempart;
5608                 gchar *boundary = NULL;
5609                 mimempart = procmime_mimeinfo_new();
5610                 mimempart->content = MIMECONTENT_EMPTY;
5611                 mimempart->type = MIMETYPE_MULTIPART;
5612                 mimempart->subtype = g_strdup("mixed");
5613
5614                 do {
5615                         g_free(boundary);
5616                         boundary = generate_mime_boundary(NULL);
5617                 } while (strstr(buf, boundary) != NULL);
5618
5619                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5620                                     boundary);
5621
5622                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5623
5624                 g_node_append(mimempart->node, mimetext->node);
5625                 g_node_append(mimemsg->node, mimempart->node);
5626
5627                 if (compose_add_attachments(compose, mimempart) < 0)
5628                         return -1;
5629         } else
5630                 g_node_append(mimemsg->node, mimetext->node);
5631
5632         g_free(buf);
5633
5634         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5635                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5636                 /* extract name and address */
5637                 if (strstr(spec, " <") && strstr(spec, ">")) {
5638                         from_addr = g_strdup(strrchr(spec, '<')+1);
5639                         *(strrchr(from_addr, '>')) = '\0';
5640                         from_name = g_strdup(spec);
5641                         *(strrchr(from_name, '<')) = '\0';
5642                 } else {
5643                         from_name = NULL;
5644                         from_addr = NULL;
5645                 }
5646                 g_free(spec);
5647         }
5648         /* sign message if sending */
5649         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5650             privacy_system_can_sign(compose->privacy_system))
5651                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5652                         compose->account, from_addr)) {
5653                         g_free(from_name);
5654                         g_free(from_addr);
5655                         return -2;
5656         }
5657         g_free(from_name);
5658         g_free(from_addr);
5659         procmime_write_mimeinfo(mimemsg, fp);
5660         
5661         procmime_mimeinfo_free_all(mimemsg);
5662
5663         return 0;
5664 }
5665
5666 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5667 {
5668         GtkTextBuffer *buffer;
5669         GtkTextIter start, end;
5670         FILE *fp;
5671         size_t len;
5672         gchar *chars, *tmp;
5673
5674         if ((fp = g_fopen(file, "wb")) == NULL) {
5675                 FILE_OP_ERROR(file, "fopen");
5676                 return -1;
5677         }
5678
5679         /* chmod for security */
5680         if (change_file_mode_rw(fp, file) < 0) {
5681                 FILE_OP_ERROR(file, "chmod");
5682                 g_warning("can't change file mode\n");
5683         }
5684
5685         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5686         gtk_text_buffer_get_start_iter(buffer, &start);
5687         gtk_text_buffer_get_end_iter(buffer, &end);
5688         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5689
5690         chars = conv_codeset_strdup
5691                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5692
5693         g_free(tmp);
5694         if (!chars) return -1;
5695
5696         /* write body */
5697         len = strlen(chars);
5698         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5699                 FILE_OP_ERROR(file, "fwrite");
5700                 g_free(chars);
5701                 fclose(fp);
5702                 claws_unlink(file);
5703                 return -1;
5704         }
5705
5706         g_free(chars);
5707
5708         if (fclose(fp) == EOF) {
5709                 FILE_OP_ERROR(file, "fclose");
5710                 claws_unlink(file);
5711                 return -1;
5712         }
5713         return 0;
5714 }
5715
5716 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5717 {
5718         FolderItem *item;
5719         MsgInfo *msginfo = compose->targetinfo;
5720
5721         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5722         if (!msginfo) return -1;
5723
5724         if (!force && MSG_IS_LOCKED(msginfo->flags))
5725                 return 0;
5726
5727         item = msginfo->folder;
5728         cm_return_val_if_fail(item != NULL, -1);
5729
5730         if (procmsg_msg_exist(msginfo) &&
5731             (folder_has_parent_of_type(item, F_QUEUE) ||
5732              folder_has_parent_of_type(item, F_DRAFT) 
5733              || msginfo == compose->autosaved_draft)) {
5734                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5735                         g_warning("can't remove the old message\n");
5736                         return -1;
5737                 } else {
5738                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5739                 }
5740         }
5741
5742         return 0;
5743 }
5744
5745 static void compose_remove_draft(Compose *compose)
5746 {
5747         FolderItem *drafts;
5748         MsgInfo *msginfo = compose->targetinfo;
5749         drafts = account_get_special_folder(compose->account, F_DRAFT);
5750
5751         if (procmsg_msg_exist(msginfo)) {
5752                 folder_item_remove_msg(drafts, msginfo->msgnum);
5753         }
5754
5755 }
5756
5757 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5758                    gboolean remove_reedit_target)
5759 {
5760         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5761 }
5762
5763 static gboolean compose_warn_encryption(Compose *compose)
5764 {
5765         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5766         AlertValue val = G_ALERTALTERNATE;
5767         
5768         if (warning == NULL)
5769                 return TRUE;
5770
5771         val = alertpanel_full(_("Encryption warning"), warning,
5772                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5773                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5774         if (val & G_ALERTDISABLE) {
5775                 val &= ~G_ALERTDISABLE;
5776                 if (val == G_ALERTALTERNATE)
5777                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5778                                 TRUE);
5779         }
5780
5781         if (val == G_ALERTALTERNATE) {
5782                 return TRUE;
5783         } else {
5784                 return FALSE;
5785         } 
5786 }
5787
5788 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5789                               gchar **msgpath, gboolean check_subject,
5790                               gboolean remove_reedit_target)
5791 {
5792         FolderItem *queue;
5793         gchar *tmp;
5794         FILE *fp;
5795         GSList *cur;
5796         gint num;
5797         PrefsAccount *mailac = NULL, *newsac = NULL;
5798         gboolean err = FALSE;
5799
5800         debug_print("queueing message...\n");
5801         cm_return_val_if_fail(compose->account != NULL, -1);
5802
5803         if (compose_check_entries(compose, check_subject) == FALSE) {
5804                 if (compose->batch) {
5805                         gtk_widget_show_all(compose->window);
5806                 }
5807                 return -1;
5808         }
5809
5810         if (!compose->to_list && !compose->newsgroup_list) {
5811                 g_warning("can't get recipient list.");
5812                 return -1;
5813         }
5814
5815         if (compose->to_list) {
5816                 if (compose->account->protocol != A_NNTP)
5817                         mailac = compose->account;
5818                 else if (cur_account && cur_account->protocol != A_NNTP)
5819                         mailac = cur_account;
5820                 else if (!(mailac = compose_current_mail_account())) {
5821                         alertpanel_error(_("No account for sending mails available!"));
5822                         return -1;
5823                 }
5824         }
5825
5826         if (compose->newsgroup_list) {
5827                 if (compose->account->protocol == A_NNTP)
5828                         newsac = compose->account;
5829                 else {
5830                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5831                         return -1;
5832                 }                       
5833         }
5834
5835         /* write queue header */
5836         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5837                               G_DIR_SEPARATOR, compose, (guint) rand());
5838         debug_print("queuing to %s\n", tmp);
5839         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5840                 FILE_OP_ERROR(tmp, "fopen");
5841                 g_free(tmp);
5842                 return -2;
5843         }
5844
5845         if (change_file_mode_rw(fp, tmp) < 0) {
5846                 FILE_OP_ERROR(tmp, "chmod");
5847                 g_warning("can't change file mode\n");
5848         }
5849
5850         /* queueing variables */
5851         err |= (fprintf(fp, "AF:\n") < 0);
5852         err |= (fprintf(fp, "NF:0\n") < 0);
5853         err |= (fprintf(fp, "PS:10\n") < 0);
5854         err |= (fprintf(fp, "SRH:1\n") < 0);
5855         err |= (fprintf(fp, "SFN:\n") < 0);
5856         err |= (fprintf(fp, "DSR:\n") < 0);
5857         if (compose->msgid)
5858                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5859         else
5860                 err |= (fprintf(fp, "MID:\n") < 0);
5861         err |= (fprintf(fp, "CFG:\n") < 0);
5862         err |= (fprintf(fp, "PT:0\n") < 0);
5863         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5864         err |= (fprintf(fp, "RQ:\n") < 0);
5865         if (mailac)
5866                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5867         else
5868                 err |= (fprintf(fp, "SSV:\n") < 0);
5869         if (newsac)
5870                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5871         else
5872                 err |= (fprintf(fp, "NSV:\n") < 0);
5873         err |= (fprintf(fp, "SSH:\n") < 0);
5874         /* write recepient list */
5875         if (compose->to_list) {
5876                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5877                 for (cur = compose->to_list->next; cur != NULL;
5878                      cur = cur->next)
5879                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5880                 err |= (fprintf(fp, "\n") < 0);
5881         }
5882         /* write newsgroup list */
5883         if (compose->newsgroup_list) {
5884                 err |= (fprintf(fp, "NG:") < 0);
5885                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5886                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5887                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5888                 err |= (fprintf(fp, "\n") < 0);
5889         }
5890         /* Sylpheed account IDs */
5891         if (mailac)
5892                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5893         if (newsac)
5894                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5895
5896         
5897         if (compose->privacy_system != NULL) {
5898                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5899                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5900                 if (compose->use_encryption) {
5901                         gchar *encdata;
5902                         if (!compose_warn_encryption(compose)) {
5903                                 fclose(fp);
5904                                 claws_unlink(tmp);
5905                                 g_free(tmp);
5906                                 return -6;
5907                         }
5908                         if (mailac && mailac->encrypt_to_self) {
5909                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5910                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5911                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5912                                 g_slist_free(tmp_list);
5913                         } else {
5914                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5915                         }
5916                         if (encdata != NULL) {
5917                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5918                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5919                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5920                                                 encdata) < 0);
5921                                 } /* else we finally dont want to encrypt */
5922                         } else {
5923                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5924                                 /* and if encdata was null, it means there's been a problem in 
5925                                  * key selection */
5926                                 if (err == TRUE)
5927                                         g_warning("failed to write queue message");
5928                                 fclose(fp);
5929                                 claws_unlink(tmp);
5930                                 g_free(tmp);
5931                                 return -5;
5932                         }
5933                         g_free(encdata);
5934                 }
5935         }
5936
5937         /* Save copy folder */
5938         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5939                 gchar *savefolderid;
5940                 
5941                 savefolderid = compose_get_save_to(compose);
5942                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5943                 g_free(savefolderid);
5944         }
5945         /* Save copy folder */
5946         if (compose->return_receipt) {
5947                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5948         }
5949         /* Message-ID of message replying to */
5950         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5951                 gchar *folderid;
5952                 
5953                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5954                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5955                 g_free(folderid);
5956         }
5957         /* Message-ID of message forwarding to */
5958         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5959                 gchar *folderid;
5960                 
5961                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5962                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5963                 g_free(folderid);
5964         }
5965
5966         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5967         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5968
5969         /* end of headers */
5970         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5971
5972         if (compose->redirect_filename != NULL) {
5973                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5974                         fclose(fp);
5975                         claws_unlink(tmp);
5976                         g_free(tmp);
5977                         return -2;
5978                 }
5979         } else {
5980                 gint result = 0;
5981                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5982                         fclose(fp);
5983                         claws_unlink(tmp);
5984                         g_free(tmp);
5985                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5986                 }
5987         }
5988         if (err == TRUE) {
5989                 g_warning("failed to write queue message\n");
5990                 fclose(fp);
5991                 claws_unlink(tmp);
5992                 g_free(tmp);
5993                 return -2;
5994         }
5995         if (fclose(fp) == EOF) {
5996                 FILE_OP_ERROR(tmp, "fclose");
5997                 claws_unlink(tmp);
5998                 g_free(tmp);
5999                 return -2;
6000         }
6001
6002         if (item && *item) {
6003                 queue = *item;
6004         } else {
6005                 queue = account_get_special_folder(compose->account, F_QUEUE);
6006         }
6007         if (!queue) {
6008                 g_warning("can't find queue folder\n");
6009                 claws_unlink(tmp);
6010                 g_free(tmp);
6011                 return -1;
6012         }
6013         folder_item_scan(queue);
6014         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6015                 g_warning("can't queue the message\n");
6016                 claws_unlink(tmp);
6017                 g_free(tmp);
6018                 return -1;
6019         }
6020         
6021         if (msgpath == NULL) {
6022                 claws_unlink(tmp);
6023                 g_free(tmp);
6024         } else
6025                 *msgpath = tmp;
6026
6027         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6028                 compose_remove_reedit_target(compose, FALSE);
6029         }
6030
6031         if ((msgnum != NULL) && (item != NULL)) {
6032                 *msgnum = num;
6033                 *item = queue;
6034         }
6035
6036         return 0;
6037 }
6038
6039 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6040 {
6041         AttachInfo *ainfo;
6042         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6043         MimeInfo *mimepart;
6044         struct stat statbuf;
6045         gchar *type, *subtype;
6046         GtkTreeModel *model;
6047         GtkTreeIter iter;
6048
6049         model = gtk_tree_view_get_model(tree_view);
6050         
6051         if (!gtk_tree_model_get_iter_first(model, &iter))
6052                 return 0;
6053         do {
6054                 gtk_tree_model_get(model, &iter,
6055                                    COL_DATA, &ainfo,
6056                                    -1);
6057                 
6058                 if (!is_file_exist(ainfo->file)) {
6059                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6060                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6061                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6062                         g_free(msg);
6063                         if (val == G_ALERTDEFAULT) {
6064                                 return -1;
6065                         }
6066                         continue;
6067                 }
6068                 mimepart = procmime_mimeinfo_new();
6069                 mimepart->content = MIMECONTENT_FILE;
6070                 mimepart->data.filename = g_strdup(ainfo->file);
6071                 mimepart->tmp = FALSE; /* or we destroy our attachment */
6072                 mimepart->offset = 0;
6073
6074                 g_stat(ainfo->file, &statbuf);
6075                 mimepart->length = statbuf.st_size;
6076
6077                 type = g_strdup(ainfo->content_type);
6078
6079                 if (!strchr(type, '/')) {
6080                         g_free(type);
6081                         type = g_strdup("application/octet-stream");
6082                 }
6083
6084                 subtype = strchr(type, '/') + 1;
6085                 *(subtype - 1) = '\0';
6086                 mimepart->type = procmime_get_media_type(type);
6087                 mimepart->subtype = g_strdup(subtype);
6088                 g_free(type);
6089
6090                 if (mimepart->type == MIMETYPE_MESSAGE && 
6091                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6092                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
6093                 } else if (mimepart->type == MIMETYPE_TEXT) {
6094                         if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6095                                 /* Text parts with no name come from multipart/alternative
6096                                 * forwards. Make sure the recipient won't look at the 
6097                                 * original HTML part by mistake. */
6098                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6099                                 ainfo->name = g_strdup_printf(_("Original %s part"),
6100                                                                 mimepart->subtype);
6101                         }
6102                         if (ainfo->charset)
6103                                 g_hash_table_insert(mimepart->typeparameters,
6104                                                     g_strdup("charset"), g_strdup(ainfo->charset));
6105                 }
6106                 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6107                         if (mimepart->type == MIMETYPE_APPLICATION && 
6108                            !strcmp2(mimepart->subtype, "octet-stream"))
6109                                 g_hash_table_insert(mimepart->typeparameters,
6110                                                 g_strdup("name"), g_strdup(ainfo->name));
6111                         g_hash_table_insert(mimepart->dispositionparameters,
6112                                         g_strdup("filename"), g_strdup(ainfo->name));
6113                         mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6114                 }
6115
6116                 if (mimepart->type == MIMETYPE_MESSAGE
6117                     || mimepart->type == MIMETYPE_MULTIPART)
6118                         ainfo->encoding = ENC_BINARY;
6119                 else if (compose->use_signing) {
6120                         if (ainfo->encoding == ENC_7BIT)
6121                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6122                         else if (ainfo->encoding == ENC_8BIT)
6123                                 ainfo->encoding = ENC_BASE64;
6124                 }
6125
6126                 
6127                 
6128                 procmime_encode_content(mimepart, ainfo->encoding);
6129
6130                 g_node_append(parent->node, mimepart->node);
6131         } while (gtk_tree_model_iter_next(model, &iter));
6132         
6133         return 0;
6134 }
6135
6136 static gchar *compose_quote_list_of_addresses(gchar *str)
6137 {
6138         GSList *list = NULL, *item = NULL;
6139         gchar *qname = NULL, *faddr = NULL, *result = NULL;
6140
6141         list = address_list_append_with_comments(list, str);
6142         for (item = list; item != NULL; item = item->next) {
6143                 gchar *spec = item->data;
6144                 gchar *endofname = strstr(spec, " <");
6145                 if (endofname != NULL) {
6146                         *endofname = '\0';
6147                         QUOTE_IF_REQUIRED_NORMAL(qname, spec, return NULL);
6148                         *endofname = ' ';
6149                         if (*qname != *spec) { /* has been quoted, compute new */
6150                                 gchar *addr = g_strdup(endofname);
6151                                 gchar *name = g_strdup(qname);
6152                                 faddr = g_strconcat(name, addr, NULL);
6153                                 g_free(name);
6154                                 g_free(addr);
6155                                 debug_print("new auto-quoted address: '%s'", faddr);
6156                         }
6157                 }
6158                 if (result == NULL)
6159                         result = g_strdup((faddr != NULL)? faddr: spec);
6160                 else {
6161                         result = g_strconcat(result,
6162                                              ", ",
6163                                              (faddr != NULL)? faddr: spec,
6164                                              NULL);
6165                 }
6166                 if (faddr != NULL) {
6167                         g_free(faddr);
6168                         faddr = NULL;
6169                 }
6170         }
6171         slist_free_strings_full(list);
6172
6173         return result;
6174 }
6175
6176 #define IS_IN_CUSTOM_HEADER(header) \
6177         (compose->account->add_customhdr && \
6178          custom_header_find(compose->account->customhdr_list, header) != NULL)
6179
6180 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6181                                                     GString *header, 
6182                                                     const gchar *fieldname,
6183                                                     const gchar *seperator)
6184 {
6185         gchar *str, *fieldname_w_colon;
6186         gboolean add_field = FALSE;
6187         GSList *list;
6188         ComposeHeaderEntry *headerentry;
6189         const gchar *headerentryname;
6190         const gchar *trans_fieldname;
6191         GString *fieldstr;
6192
6193         if (IS_IN_CUSTOM_HEADER(fieldname))
6194                 return;
6195
6196         debug_print("Adding %s-fields\n", fieldname);
6197
6198         fieldstr = g_string_sized_new(64);
6199
6200         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6201         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6202
6203         for (list = compose->header_list; list; list = list->next) {
6204                 headerentry = ((ComposeHeaderEntry *)list->data);
6205                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6206
6207                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6208                         gchar * ustr = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6209                         g_strstrip(ustr);
6210                         str = compose_quote_list_of_addresses(ustr);
6211                         g_free(ustr);
6212                         if (str != NULL && str[0] != '\0') {
6213                                 if (add_field)
6214                                         g_string_append(fieldstr, seperator);
6215                                 g_string_append(fieldstr, str);
6216                                 add_field = TRUE;
6217                         }
6218                         g_free(str);
6219                 }
6220         }
6221         if (add_field) {
6222                 gchar *buf;
6223
6224                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6225                 compose_convert_header
6226                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6227                         strlen(fieldname) + 2, TRUE);
6228                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6229                 g_free(buf);
6230         }
6231
6232         g_free(fieldname_w_colon);
6233         g_string_free(fieldstr, TRUE);
6234
6235         return;
6236 }
6237
6238 static gchar *compose_get_manual_headers_info(Compose *compose)
6239 {
6240         GString *sh_header = g_string_new(" ");
6241         GSList *list;
6242         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6243
6244         for (list = compose->header_list; list; list = list->next) {
6245                 ComposeHeaderEntry *headerentry;
6246                 gchar *tmp;
6247                 gchar *headername;
6248                 gchar *headername_wcolon;
6249                 const gchar *headername_trans;
6250                 gchar **string;
6251                 gboolean standard_header = FALSE;
6252
6253                 headerentry = ((ComposeHeaderEntry *)list->data);
6254
6255                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6256                 g_strstrip(tmp);
6257                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6258                         g_free(tmp);
6259                         continue;
6260                 }
6261
6262                 if (!strstr(tmp, ":")) {
6263                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6264                         headername = g_strdup(tmp);
6265                 } else {
6266                         headername_wcolon = g_strdup(tmp);
6267                         headername = g_strdup(strtok(tmp, ":"));
6268                 }
6269                 g_free(tmp);
6270                 
6271                 string = std_headers;
6272                 while (*string != NULL) {
6273                         headername_trans = prefs_common_translated_header_name(*string);
6274                         if (!strcmp(headername_trans, headername_wcolon))
6275                                 standard_header = TRUE;
6276                         string++;
6277                 }
6278                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6279                         g_string_append_printf(sh_header, "%s ", headername);
6280                 g_free(headername);
6281                 g_free(headername_wcolon);
6282         }
6283         g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6284         return g_string_free(sh_header, FALSE);
6285 }
6286
6287 static gchar *compose_get_header(Compose *compose)
6288 {
6289         gchar buf[BUFFSIZE];
6290         const gchar *entry_str;
6291         gchar *str;
6292         gchar *name;
6293         GSList *list;
6294         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6295         GString *header;
6296         gchar *from_name = NULL, *from_address = NULL;
6297         gchar *tmp;
6298
6299         cm_return_val_if_fail(compose->account != NULL, NULL);
6300         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6301
6302         header = g_string_sized_new(64);
6303
6304         /* Date */
6305         get_rfc822_date(buf, sizeof(buf));
6306         g_string_append_printf(header, "Date: %s\n", buf);
6307
6308         /* From */
6309         
6310         if (compose->account->name && *compose->account->name) {
6311                 gchar *buf;
6312                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6313                 tmp = g_strdup_printf("%s <%s>",
6314                         buf, compose->account->address);
6315         } else {
6316                 tmp = g_strdup_printf("%s",
6317                         compose->account->address);
6318         }
6319         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6320         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6321                 /* use default */
6322                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6323                 from_address = g_strdup(compose->account->address);
6324         } else {
6325                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6326                 /* extract name and address */
6327                 if (strstr(spec, " <") && strstr(spec, ">")) {
6328                         from_address = g_strdup(strrchr(spec, '<')+1);
6329                         *(strrchr(from_address, '>')) = '\0';
6330                         from_name = g_strdup(spec);
6331                         *(strrchr(from_name, '<')) = '\0';
6332                 } else {
6333                         from_name = NULL;
6334                         from_address = g_strdup(spec);
6335                 }
6336                 g_free(spec);
6337         }
6338         g_free(tmp);
6339         
6340         
6341         if (from_name && *from_name) {
6342                 compose_convert_header
6343                         (compose, buf, sizeof(buf), from_name,
6344                          strlen("From: "), TRUE);
6345                 QUOTE_IF_REQUIRED(name, buf);
6346                 
6347                 g_string_append_printf(header, "From: %s <%s>\n",
6348                         name, from_address);
6349         } else
6350                 g_string_append_printf(header, "From: %s\n", from_address);
6351         
6352         g_free(from_name);
6353         g_free(from_address);
6354
6355         /* To */
6356         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6357
6358         /* Newsgroups */
6359         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6360
6361         /* Cc */
6362         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6363
6364         /* Bcc */
6365         /* 
6366          * If this account is a NNTP account remove Bcc header from 
6367          * message body since it otherwise will be publicly shown
6368          */
6369         if (compose->account->protocol != A_NNTP)
6370                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6371
6372         /* Subject */
6373         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6374
6375         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6376                 g_strstrip(str);
6377                 if (*str != '\0') {
6378                         compose_convert_header(compose, buf, sizeof(buf), str,
6379                                                strlen("Subject: "), FALSE);
6380                         g_string_append_printf(header, "Subject: %s\n", buf);
6381                 }
6382         }
6383         g_free(str);
6384
6385         /* Message-ID */
6386         if (compose->account->set_domain && compose->account->domain) {
6387                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
6388         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6389                 g_snprintf(buf, sizeof(buf), "%s", 
6390                         strchr(compose->account->address, '@') ?
6391                                 strchr(compose->account->address, '@')+1 :
6392                                 compose->account->address);
6393         } else {
6394                 g_snprintf(buf, sizeof(buf), "%s", "");
6395         }
6396         
6397         if (compose->account->gen_msgid) {
6398                 gchar *addr = NULL;
6399                 if (compose->account->msgid_with_addr) {
6400                         addr = compose->account->address;
6401                 }
6402                 generate_msgid(buf, sizeof(buf), addr);
6403                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6404                 compose->msgid = g_strdup(buf);
6405         } else {
6406                 compose->msgid = NULL;
6407         }
6408
6409         if (compose->remove_references == FALSE) {
6410                 /* In-Reply-To */
6411                 if (compose->inreplyto && compose->to_list)
6412                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6413         
6414                 /* References */
6415                 if (compose->references)
6416                         g_string_append_printf(header, "References: %s\n", compose->references);
6417         }
6418
6419         /* Followup-To */
6420         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6421
6422         /* Reply-To */
6423         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6424
6425         /* Organization */
6426         if (compose->account->organization &&
6427             strlen(compose->account->organization) &&
6428             !IS_IN_CUSTOM_HEADER("Organization")) {
6429                 compose_convert_header(compose, buf, sizeof(buf),
6430                                        compose->account->organization,
6431                                        strlen("Organization: "), FALSE);
6432                 g_string_append_printf(header, "Organization: %s\n", buf);
6433         }
6434
6435         /* Program version and system info */
6436         if (compose->account->gen_xmailer &&
6437             g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6438             !compose->newsgroup_list) {
6439                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6440                         prog_version,
6441                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6442                         TARGET_ALIAS);
6443         }
6444         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6445                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6446                         prog_version,
6447                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6448                         TARGET_ALIAS);
6449         }
6450
6451         /* custom headers */
6452         if (compose->account->add_customhdr) {
6453                 GSList *cur;
6454
6455                 for (cur = compose->account->customhdr_list; cur != NULL;
6456                      cur = cur->next) {
6457                         CustomHeader *chdr = (CustomHeader *)cur->data;
6458
6459                         if (custom_header_is_allowed(chdr->name)
6460                             && chdr->value != NULL
6461                             && *(chdr->value) != '\0') {
6462                                 compose_convert_header
6463                                         (compose, buf, sizeof(buf),
6464                                          chdr->value,
6465                                          strlen(chdr->name) + 2, FALSE);
6466                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6467                         }
6468                 }
6469         }
6470
6471         /* Automatic Faces and X-Faces */
6472         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6473                 g_string_append_printf(header, "X-Face: %s\n", buf);
6474         }
6475         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6476                 g_string_append_printf(header, "X-Face: %s\n", buf);
6477         }
6478         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6479                 g_string_append_printf(header, "Face: %s\n", buf);
6480         }
6481         else if (get_default_face (buf, sizeof(buf)) == 0) {
6482                 g_string_append_printf(header, "Face: %s\n", buf);
6483         }
6484
6485         /* PRIORITY */
6486         switch (compose->priority) {
6487                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6488                                                    "X-Priority: 1 (Highest)\n");
6489                         break;
6490                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6491                                                 "X-Priority: 2 (High)\n");
6492                         break;
6493                 case PRIORITY_NORMAL: break;
6494                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6495                                                "X-Priority: 4 (Low)\n");
6496                         break;
6497                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6498                                                   "X-Priority: 5 (Lowest)\n");
6499                         break;
6500                 default: debug_print("compose: priority unknown : %d\n",
6501                                      compose->priority);
6502         }
6503
6504         /* Request Return Receipt */
6505         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6506                 if (compose->return_receipt) {
6507                         if (compose->account->name
6508                             && *compose->account->name) {
6509                                 compose_convert_header(compose, buf, sizeof(buf), 
6510                                                        compose->account->name, 
6511                                                        strlen("Disposition-Notification-To: "),
6512                                                        TRUE);
6513                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6514                         } else
6515                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6516                 }
6517         }
6518
6519         /* get special headers */
6520         for (list = compose->header_list; list; list = list->next) {
6521                 ComposeHeaderEntry *headerentry;
6522                 gchar *tmp;
6523                 gchar *headername;
6524                 gchar *headername_wcolon;
6525                 const gchar *headername_trans;
6526                 gchar *headervalue;
6527                 gchar **string;
6528                 gboolean standard_header = FALSE;
6529
6530                 headerentry = ((ComposeHeaderEntry *)list->data);
6531
6532                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6533                 g_strstrip(tmp);
6534                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6535                         g_free(tmp);
6536                         continue;
6537                 }
6538
6539                 if (!strstr(tmp, ":")) {
6540                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6541                         headername = g_strdup(tmp);
6542                 } else {
6543                         headername_wcolon = g_strdup(tmp);
6544                         headername = g_strdup(strtok(tmp, ":"));
6545                 }
6546                 g_free(tmp);
6547                 
6548                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6549                 Xstrdup_a(headervalue, entry_str, return NULL);
6550                 subst_char(headervalue, '\r', ' ');
6551                 subst_char(headervalue, '\n', ' ');
6552                 string = std_headers;
6553                 while (*string != NULL) {
6554                         headername_trans = prefs_common_translated_header_name(*string);
6555                         if (!strcmp(headername_trans, headername_wcolon))
6556                                 standard_header = TRUE;
6557                         string++;
6558                 }
6559                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6560                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6561                                 
6562                 g_free(headername);
6563                 g_free(headername_wcolon);              
6564         }
6565
6566         str = header->str;
6567         g_string_free(header, FALSE);
6568
6569         return str;
6570 }
6571
6572 #undef IS_IN_CUSTOM_HEADER
6573
6574 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6575                                    gint header_len, gboolean addr_field)
6576 {
6577         gchar *tmpstr = NULL;
6578         const gchar *out_codeset = NULL;
6579
6580         cm_return_if_fail(src != NULL);
6581         cm_return_if_fail(dest != NULL);
6582
6583         if (len < 1) return;
6584
6585         tmpstr = g_strdup(src);
6586
6587         subst_char(tmpstr, '\n', ' ');
6588         subst_char(tmpstr, '\r', ' ');
6589         g_strchomp(tmpstr);
6590
6591         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6592                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6593                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6594                 g_free(tmpstr);
6595                 tmpstr = mybuf;
6596         }
6597
6598         codeconv_set_strict(TRUE);
6599         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6600                 conv_get_charset_str(compose->out_encoding));
6601         codeconv_set_strict(FALSE);
6602         
6603         if (!dest || *dest == '\0') {
6604                 gchar *test_conv_global_out = NULL;
6605                 gchar *test_conv_reply = NULL;
6606
6607                 /* automatic mode. be automatic. */
6608                 codeconv_set_strict(TRUE);
6609
6610                 out_codeset = conv_get_outgoing_charset_str();
6611                 if (out_codeset) {
6612                         debug_print("trying to convert to %s\n", out_codeset);
6613                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6614                 }
6615
6616                 if (!test_conv_global_out && compose->orig_charset
6617                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6618                         out_codeset = compose->orig_charset;
6619                         debug_print("failure; trying to convert to %s\n", out_codeset);
6620                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6621                 }
6622
6623                 if (!test_conv_global_out && !test_conv_reply) {
6624                         /* we're lost */
6625                         out_codeset = CS_INTERNAL;
6626                         debug_print("finally using %s\n", out_codeset);
6627                 }
6628                 g_free(test_conv_global_out);
6629                 g_free(test_conv_reply);
6630                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6631                                         out_codeset);
6632                 codeconv_set_strict(FALSE);
6633         }
6634         g_free(tmpstr);
6635 }
6636
6637 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6638 {
6639         gchar *address;
6640
6641         cm_return_if_fail(user_data != NULL);
6642
6643         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6644         g_strstrip(address);
6645         if (*address != '\0') {
6646                 gchar *name = procheader_get_fromname(address);
6647                 extract_address(address);
6648 #ifndef USE_NEW_ADDRBOOK
6649                 addressbook_add_contact(name, address, NULL, NULL);
6650 #else
6651                 debug_print("%s: %s\n", name, address);
6652                 if (addressadd_selection(name, address, NULL, NULL)) {
6653                         debug_print( "addressbook_add_contact - added\n" );
6654                 }
6655 #endif
6656         }
6657         g_free(address);
6658 }
6659
6660 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6661 {
6662         GtkWidget *menuitem;
6663         gchar *address;
6664
6665         cm_return_if_fail(menu != NULL);
6666         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6667
6668         menuitem = gtk_separator_menu_item_new();
6669         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6670         gtk_widget_show(menuitem);
6671
6672         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6673         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6674
6675         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6676         g_strstrip(address);
6677         if (*address == '\0') {
6678                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6679         }
6680
6681         g_signal_connect(G_OBJECT(menuitem), "activate",
6682                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6683         gtk_widget_show(menuitem);
6684 }
6685
6686 void compose_add_extra_header(gchar *header, GtkListStore *model)
6687 {
6688         GtkTreeIter iter;
6689         if (strcmp(header, "")) {
6690                 COMBOBOX_ADD(model, header, COMPOSE_TO);
6691         }
6692 }
6693
6694 void compose_add_extra_header_entries(GtkListStore *model)
6695 {
6696         FILE *exh;
6697         gchar *exhrc;
6698         gchar buf[BUFFSIZE];
6699         gint lastc;
6700
6701         if (extra_headers == NULL) {
6702                 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6703                 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6704                         debug_print("extra headers file not found\n");
6705                         goto extra_headers_done;
6706                 }
6707                 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6708                         lastc = strlen(buf) - 1;        /* remove trailing control chars */
6709                         while (lastc >= 0 && buf[lastc] != ':')
6710                                 buf[lastc--] = '\0';
6711                         if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6712                                 buf[lastc] = '\0'; /* remove trailing : for comparison */
6713                                 if (custom_header_is_allowed(buf)) {
6714                                         buf[lastc] = ':';
6715                                         extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6716                                 }
6717                                 else
6718                                         g_message("disallowed extra header line: %s\n", buf);
6719                         }
6720                         else {
6721                                 if (buf[0] != '#')
6722                                         g_message("invalid extra header line: %s\n", buf);
6723                         }
6724                 }
6725                 fclose(exh);
6726 extra_headers_done:
6727                 g_free(exhrc);
6728                 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6729                 extra_headers = g_slist_reverse(extra_headers);
6730         }
6731         g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6732 }
6733
6734 static void compose_create_header_entry(Compose *compose) 
6735 {
6736         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6737
6738         GtkWidget *combo;
6739         GtkWidget *entry;
6740         GtkWidget *button;
6741         GtkWidget *hbox;
6742         gchar **string;
6743         const gchar *header = NULL;
6744         ComposeHeaderEntry *headerentry;
6745         gboolean standard_header = FALSE;
6746         GtkListStore *model;
6747         GtkTreeIter iter;
6748 #if !(GTK_CHECK_VERSION(2,12,0))
6749         GtkTooltips *tips = compose->tooltips;
6750 #endif
6751         
6752         headerentry = g_new0(ComposeHeaderEntry, 1);
6753
6754         /* Combo box model */
6755         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6756 #if !GTK_CHECK_VERSION(2, 24, 0)
6757         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6758 #endif
6759         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6760                         COMPOSE_TO);
6761         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6762                         COMPOSE_CC);
6763         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6764                         COMPOSE_BCC);
6765         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6766                         COMPOSE_NEWSGROUPS);                    
6767         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6768                         COMPOSE_REPLYTO);
6769         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6770                         COMPOSE_FOLLOWUPTO);
6771         compose_add_extra_header_entries(model);
6772
6773         /* Combo box */
6774 #if GTK_CHECK_VERSION(2, 24, 0)
6775         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6776         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6777         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6778         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6779         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6780 #endif
6781         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6782         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6783                          G_CALLBACK(compose_grab_focus_cb), compose);
6784         gtk_widget_show(combo);
6785         
6786         GList *l = NULL;
6787         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6788         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6789         g_list_free(l);
6790
6791         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6792                         compose->header_nextrow, compose->header_nextrow+1,
6793                         GTK_SHRINK, GTK_FILL, 0, 0);
6794         if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6795                 const gchar *last_header_entry = gtk_entry_get_text(
6796                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6797                 string = headers;
6798                 while (*string != NULL) {
6799                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6800                                 standard_header = TRUE;
6801                         string++;
6802                 }
6803                 if (standard_header)
6804                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6805         }
6806         if (!compose->header_last || !standard_header) {
6807                 switch(compose->account->protocol) {
6808                         case A_NNTP:
6809                                 header = prefs_common_translated_header_name("Newsgroups:");
6810                                 break;
6811                         default:
6812                                 header = prefs_common_translated_header_name("To:");
6813                                 break;
6814                 }                                                                   
6815         }
6816         if (header)
6817                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6818
6819         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6820                          G_CALLBACK(compose_grab_focus_cb), compose);
6821
6822         /* Entry field with cleanup button */
6823         button = gtk_button_new();
6824         gtk_button_set_image(GTK_BUTTON(button),
6825                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6826         gtk_widget_show(button);
6827         CLAWS_SET_TIP(button,
6828                 _("Delete entry contents"));
6829         entry = gtk_entry_new(); 
6830         gtk_widget_show(entry);
6831         CLAWS_SET_TIP(entry,
6832                 _("Use <tab> to autocomplete from addressbook"));
6833         hbox = gtk_hbox_new (FALSE, 0);
6834         gtk_widget_show(hbox);
6835         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6836         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6837         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6838                         compose->header_nextrow, compose->header_nextrow+1,
6839                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6840
6841         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6842                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6843                          headerentry);
6844         g_signal_connect(G_OBJECT(entry), "changed", 
6845                          G_CALLBACK(compose_headerentry_changed_cb), 
6846                          headerentry);
6847         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6848                          G_CALLBACK(compose_grab_focus_cb), compose);
6849
6850         g_signal_connect(G_OBJECT(button), "clicked",
6851                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6852                          headerentry); 
6853                          
6854         /* email dnd */
6855         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6856                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6857                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6858         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6859                          G_CALLBACK(compose_header_drag_received_cb),
6860                          entry);
6861         g_signal_connect(G_OBJECT(entry), "drag-drop",
6862                          G_CALLBACK(compose_drag_drop),
6863                          compose);
6864         g_signal_connect(G_OBJECT(entry), "populate-popup",
6865                          G_CALLBACK(compose_entry_popup_extend),
6866                          NULL);
6867         
6868         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6869
6870         headerentry->compose = compose;
6871         headerentry->combo = combo;
6872         headerentry->entry = entry;
6873         headerentry->button = button;
6874         headerentry->hbox = hbox;
6875         headerentry->headernum = compose->header_nextrow;
6876         headerentry->type = PREF_NONE;
6877
6878         compose->header_nextrow++;
6879         compose->header_last = headerentry;             
6880         compose->header_list =
6881                 g_slist_append(compose->header_list,
6882                                headerentry);
6883 }
6884
6885 static void compose_add_header_entry(Compose *compose, const gchar *header,
6886                                 gchar *text, ComposePrefType pref_type) 
6887 {
6888         ComposeHeaderEntry *last_header = compose->header_last;
6889         gchar *tmp = g_strdup(text), *email;
6890         gboolean replyto_hdr;
6891         
6892         replyto_hdr = (!strcasecmp(header,
6893                                 prefs_common_translated_header_name("Reply-To:")) ||
6894                         !strcasecmp(header,
6895                                 prefs_common_translated_header_name("Followup-To:")) ||
6896                         !strcasecmp(header,
6897                                 prefs_common_translated_header_name("In-Reply-To:")));
6898                 
6899         extract_address(tmp);
6900         email = g_utf8_strdown(tmp, -1);
6901         
6902         if (replyto_hdr == FALSE &&
6903             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6904         {
6905                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6906                                 header, text, (gint) pref_type);
6907                 g_free(email);
6908                 g_free(tmp);
6909                 return;
6910         }
6911         
6912         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6913                 gtk_entry_set_text(GTK_ENTRY(
6914                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6915         else
6916                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6917         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6918         last_header->type = pref_type;
6919
6920         if (replyto_hdr == FALSE)
6921                 g_hash_table_insert(compose->email_hashtable, email,
6922                                     GUINT_TO_POINTER(1));
6923         else
6924                 g_free(email);
6925         
6926         g_free(tmp);
6927 }
6928
6929 static void compose_destroy_headerentry(Compose *compose, 
6930                                         ComposeHeaderEntry *headerentry)
6931 {
6932         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6933         gchar *email;
6934
6935         extract_address(text);
6936         email = g_utf8_strdown(text, -1);
6937         g_hash_table_remove(compose->email_hashtable, email);
6938         g_free(text);
6939         g_free(email);
6940         
6941         gtk_widget_destroy(headerentry->combo);
6942         gtk_widget_destroy(headerentry->entry);
6943         gtk_widget_destroy(headerentry->button);
6944         gtk_widget_destroy(headerentry->hbox);
6945         g_free(headerentry);
6946 }
6947
6948 static void compose_remove_header_entries(Compose *compose) 
6949 {
6950         GSList *list;
6951         for (list = compose->header_list; list; list = list->next)
6952                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6953
6954         compose->header_last = NULL;
6955         g_slist_free(compose->header_list);
6956         compose->header_list = NULL;
6957         compose->header_nextrow = 1;
6958         compose_create_header_entry(compose);
6959 }
6960
6961 static GtkWidget *compose_create_header(Compose *compose) 
6962 {
6963         GtkWidget *from_optmenu_hbox;
6964         GtkWidget *header_scrolledwin_main;
6965         GtkWidget *header_table_main;
6966         GtkWidget *header_scrolledwin;
6967         GtkWidget *header_table;
6968
6969         /* parent with account selection and from header */
6970         header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6971         gtk_widget_show(header_scrolledwin_main);
6972         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6973
6974         header_table_main = gtk_table_new(2, 2, FALSE);
6975         gtk_widget_show(header_table_main);
6976         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6977         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6978         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6979
6980         from_optmenu_hbox = compose_account_option_menu_create(compose);
6981         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6982                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6983
6984         /* child with header labels and entries */
6985         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6986         gtk_widget_show(header_scrolledwin);
6987         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6988
6989         header_table = gtk_table_new(2, 2, FALSE);
6990         gtk_widget_show(header_table);
6991         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6992         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6993         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6994
6995         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6996                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6997
6998         compose->header_table = header_table;
6999         compose->header_list = NULL;
7000         compose->header_nextrow = 0;
7001
7002         compose_create_header_entry(compose);
7003
7004         compose->table = NULL;
7005
7006         return header_scrolledwin_main;
7007 }
7008
7009 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7010 {
7011         Compose *compose = (Compose *)data;
7012         GdkEventButton event;
7013         
7014         event.button = 3;
7015         event.time = gtk_get_current_event_time();
7016
7017         return attach_button_pressed(compose->attach_clist, &event, compose);
7018 }
7019
7020 static GtkWidget *compose_create_attach(Compose *compose)
7021 {
7022         GtkWidget *attach_scrwin;
7023         GtkWidget *attach_clist;
7024
7025         GtkListStore *store;
7026         GtkCellRenderer *renderer;
7027         GtkTreeViewColumn *column;
7028         GtkTreeSelection *selection;
7029
7030         /* attachment list */
7031         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7032         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7033                                        GTK_POLICY_AUTOMATIC,
7034                                        GTK_POLICY_AUTOMATIC);
7035         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7036
7037         store = gtk_list_store_new(N_ATTACH_COLS, 
7038                                    G_TYPE_STRING,
7039                                    G_TYPE_STRING,
7040                                    G_TYPE_STRING,
7041                                    G_TYPE_STRING,
7042                                    G_TYPE_POINTER,
7043                                    G_TYPE_AUTO_POINTER,
7044                                    -1);
7045         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7046                                         (GTK_TREE_MODEL(store)));
7047         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7048         g_object_unref(store);
7049         
7050         renderer = gtk_cell_renderer_text_new();
7051         column = gtk_tree_view_column_new_with_attributes
7052                         (_("Mime type"), renderer, "text", 
7053                          COL_MIMETYPE, NULL);
7054         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7055         
7056         renderer = gtk_cell_renderer_text_new();
7057         column = gtk_tree_view_column_new_with_attributes
7058                         (_("Size"), renderer, "text", 
7059                          COL_SIZE, NULL);
7060         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7061         
7062         renderer = gtk_cell_renderer_text_new();
7063         column = gtk_tree_view_column_new_with_attributes
7064                         (_("Name"), renderer, "text", 
7065                          COL_NAME, NULL);
7066         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7067
7068         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7069                                      prefs_common.use_stripes_everywhere);
7070         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7071         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7072
7073         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7074                          G_CALLBACK(attach_selected), compose);
7075         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7076                          G_CALLBACK(attach_button_pressed), compose);
7077         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7078                          G_CALLBACK(popup_attach_button_pressed), compose);
7079         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7080                          G_CALLBACK(attach_key_pressed), compose);
7081
7082         /* drag and drop */
7083         gtk_drag_dest_set(attach_clist,
7084                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7085                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7086                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7087         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7088                          G_CALLBACK(compose_attach_drag_received_cb),
7089                          compose);
7090         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7091                          G_CALLBACK(compose_drag_drop),
7092                          compose);
7093
7094         compose->attach_scrwin = attach_scrwin;
7095         compose->attach_clist  = attach_clist;
7096
7097         return attach_scrwin;
7098 }
7099
7100 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7101 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7102
7103 static GtkWidget *compose_create_others(Compose *compose)
7104 {
7105         GtkWidget *table;
7106         GtkWidget *savemsg_checkbtn;
7107         GtkWidget *savemsg_combo;
7108         GtkWidget *savemsg_select;
7109         
7110         guint rowcount = 0;
7111         gchar *folderidentifier;
7112
7113         /* Table for settings */
7114         table = gtk_table_new(3, 1, FALSE);
7115         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7116         gtk_widget_show(table);
7117         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7118         rowcount = 0;
7119
7120         /* Save Message to folder */
7121         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7122         gtk_widget_show(savemsg_checkbtn);
7123         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7124         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7125                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7126         }
7127         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7128                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7129
7130 #if !GTK_CHECK_VERSION(2, 24, 0)
7131         savemsg_combo = gtk_combo_box_entry_new_text();
7132 #else
7133         savemsg_combo = gtk_combo_box_text_new_with_entry();
7134 #endif
7135         compose->savemsg_checkbtn = savemsg_checkbtn;
7136         compose->savemsg_combo = savemsg_combo;
7137         gtk_widget_show(savemsg_combo);
7138
7139         if (prefs_common.compose_save_to_history)
7140 #if !GTK_CHECK_VERSION(2, 24, 0)
7141                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7142                                 prefs_common.compose_save_to_history);
7143 #else
7144                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7145                                 prefs_common.compose_save_to_history);
7146 #endif
7147         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7148         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7149         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7150                          G_CALLBACK(compose_grab_focus_cb), compose);
7151         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7152                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7153                                   (compose->account, F_OUTBOX));
7154                 compose_set_save_to(compose, folderidentifier);
7155                 g_free(folderidentifier);
7156         }
7157
7158         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7159         gtk_widget_show(savemsg_select);
7160         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7161         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7162                          G_CALLBACK(compose_savemsg_select_cb),
7163                          compose);
7164
7165         return table;   
7166 }
7167
7168 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7169 {
7170         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7171                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7172 }
7173
7174 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7175 {
7176         FolderItem *dest;
7177         gchar * path;
7178
7179         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7180         if (!dest) return;
7181
7182         path = folder_item_get_identifier(dest);
7183
7184         compose_set_save_to(compose, path);
7185         g_free(path);
7186 }
7187
7188 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7189                                   GdkAtom clip, GtkTextIter *insert_place);
7190
7191
7192 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7193                                        Compose *compose)
7194 {
7195         gint prev_autowrap;
7196         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7197 #if USE_ENCHANT
7198         if (event->button == 3) {
7199                 GtkTextIter iter;
7200                 GtkTextIter sel_start, sel_end;
7201                 gboolean stuff_selected;
7202                 gint x, y;
7203                 /* move the cursor to allow GtkAspell to check the word
7204                  * under the mouse */
7205                 if (event->x && event->y) {
7206                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7207                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7208                                 &x, &y);
7209                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7210                                 &iter, x, y);
7211                 } else {
7212                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7213                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7214                 }
7215                 /* get selection */
7216                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7217                                 buffer,
7218                                 &sel_start, &sel_end);
7219
7220                 gtk_text_buffer_place_cursor (buffer, &iter);
7221                 /* reselect stuff */
7222                 if (stuff_selected 
7223                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7224                         gtk_text_buffer_select_range(buffer,
7225                                 &sel_start, &sel_end);
7226                 }
7227                 return FALSE; /* pass the event so that the right-click goes through */
7228         }
7229 #endif
7230         if (event->button == 2) {
7231                 GtkTextIter iter;
7232                 gint x, y;
7233                 BLOCK_WRAP();
7234                 
7235                 /* get the middle-click position to paste at the correct place */
7236                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7237                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7238                         &x, &y);
7239                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7240                         &iter, x, y);
7241                 
7242                 entry_paste_clipboard(compose, text, 
7243                                 prefs_common.linewrap_pastes,
7244                                 GDK_SELECTION_PRIMARY, &iter);
7245                 UNBLOCK_WRAP();
7246                 return TRUE;
7247         }
7248         return FALSE;
7249 }
7250
7251 #if USE_ENCHANT
7252 static void compose_spell_menu_changed(void *data)
7253 {
7254         Compose *compose = (Compose *)data;
7255         GSList *items;
7256         GtkWidget *menuitem;
7257         GtkWidget *parent_item;
7258         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7259         GSList *spell_menu;
7260
7261         if (compose->gtkaspell == NULL)
7262                 return;
7263
7264         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7265                         "/Menu/Spelling/Options");
7266
7267         /* setting the submenu removes /Spelling/Options from the factory 
7268          * so we need to save it */
7269
7270         if (parent_item == NULL) {
7271                 parent_item = compose->aspell_options_menu;
7272                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7273         } else
7274                 compose->aspell_options_menu = parent_item;
7275
7276         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7277
7278         spell_menu = g_slist_reverse(spell_menu);
7279         for (items = spell_menu;
7280              items; items = items->next) {
7281                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7282                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7283                 gtk_widget_show(GTK_WIDGET(menuitem));
7284         }
7285         g_slist_free(spell_menu);
7286
7287         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7288         gtk_widget_show(parent_item);
7289 }
7290
7291 static void compose_dict_changed(void *data)
7292 {
7293         Compose *compose = (Compose *) data;
7294
7295         if(compose->gtkaspell && 
7296            compose->gtkaspell->recheck_when_changing_dict == FALSE)
7297                 return;
7298
7299         gtkaspell_highlight_all(compose->gtkaspell);
7300         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7301 }
7302 #endif
7303
7304 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7305 {
7306         Compose *compose = (Compose *)data;
7307         GdkEventButton event;
7308         
7309         event.button = 3;
7310         event.time = gtk_get_current_event_time();
7311         event.x = 0;
7312         event.y = 0;
7313
7314         return text_clicked(compose->text, &event, compose);
7315 }
7316
7317 static gboolean compose_force_window_origin = TRUE;
7318 static Compose *compose_create(PrefsAccount *account,
7319                                                  FolderItem *folder,
7320                                                  ComposeMode mode,
7321                                                  gboolean batch)
7322 {
7323         Compose   *compose;
7324         GtkWidget *window;
7325         GtkWidget *vbox;
7326         GtkWidget *menubar;
7327         GtkWidget *handlebox;
7328
7329         GtkWidget *notebook;
7330         
7331         GtkWidget *attach_hbox;
7332         GtkWidget *attach_lab1;
7333         GtkWidget *attach_lab2;
7334
7335         GtkWidget *vbox2;
7336
7337         GtkWidget *label;
7338         GtkWidget *subject_hbox;
7339         GtkWidget *subject_frame;
7340         GtkWidget *subject_entry;
7341         GtkWidget *subject;
7342         GtkWidget *paned;
7343
7344         GtkWidget *edit_vbox;
7345         GtkWidget *ruler_hbox;
7346         GtkWidget *ruler;
7347         GtkWidget *scrolledwin;
7348         GtkWidget *text;
7349         GtkTextBuffer *buffer;
7350         GtkClipboard *clipboard;
7351
7352         UndoMain *undostruct;
7353
7354         GtkWidget *popupmenu;
7355         GtkWidget *tmpl_menu;
7356         GtkActionGroup *action_group = NULL;
7357
7358 #if USE_ENCHANT
7359         GtkAspell * gtkaspell = NULL;
7360 #endif
7361
7362         static GdkGeometry geometry;
7363
7364         cm_return_val_if_fail(account != NULL, NULL);
7365
7366         debug_print("Creating compose window...\n");
7367         compose = g_new0(Compose, 1);
7368
7369         compose->batch = batch;
7370         compose->account = account;
7371         compose->folder = folder;
7372         
7373         compose->mutex = cm_mutex_new();
7374         compose->set_cursor_pos = -1;
7375
7376 #if !(GTK_CHECK_VERSION(2,12,0))
7377         compose->tooltips = tips;
7378 #endif
7379
7380         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7381
7382         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7383         gtk_widget_set_size_request(window, prefs_common.compose_width,
7384                                         prefs_common.compose_height);
7385
7386         if (!geometry.max_width) {
7387                 geometry.max_width = gdk_screen_width();
7388                 geometry.max_height = gdk_screen_height();
7389         }
7390
7391         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7392                                       &geometry, GDK_HINT_MAX_SIZE);
7393         if (!geometry.min_width) {
7394                 geometry.min_width = 600;
7395                 geometry.min_height = 440;
7396         }
7397         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7398                                       &geometry, GDK_HINT_MIN_SIZE);
7399
7400 #ifndef GENERIC_UMPC    
7401         if (compose_force_window_origin)
7402                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7403                                  prefs_common.compose_y);
7404 #endif
7405         g_signal_connect(G_OBJECT(window), "delete_event",
7406                          G_CALLBACK(compose_delete_cb), compose);
7407         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7408         gtk_widget_realize(window);
7409
7410         gtkut_widget_set_composer_icon(window);
7411
7412         vbox = gtk_vbox_new(FALSE, 0);
7413         gtk_container_add(GTK_CONTAINER(window), vbox);
7414
7415         compose->ui_manager = gtk_ui_manager_new();
7416         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7417                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7418         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7419                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7420         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7421                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7422         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7423                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7424         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7425                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7426
7427         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7428
7429         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7430         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7431 #ifdef USE_ENCHANT
7432         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7433 #endif
7434         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7435         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7436         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7437
7438 /* Compose menu */
7439         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7440         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7441         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7442         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7443         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7444         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7445         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7446         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7447         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7448         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7449         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7450         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7451         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7452
7453 /* Edit menu */
7454         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7455         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7456         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7457
7458         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7459         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7460         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7461
7462         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7463         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7464         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7465         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7466
7467         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7468
7469         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7470         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7471         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7472         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7473         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7474         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7475         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7476         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7477         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7478         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7479         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7480         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7481         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7482         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7483         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7484
7485         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7486
7487         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7488         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7489         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7490         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7491         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7492
7493         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7494
7495         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7496
7497 #if USE_ENCHANT
7498 /* Spelling menu */
7499         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7500         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7501         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7502         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7503         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7504         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7505 #endif
7506
7507 /* Options menu */
7508         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7509         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7510         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7511         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7512         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7513
7514         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7515         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7516         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7517         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7518         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7519
7520         
7521         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7522         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7523         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7524         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7525         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7526         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7527         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7528
7529         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7530         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7531         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7532         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7533         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7534
7535         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7536
7537         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7538         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7539         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7540         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7541         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7542
7543         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7544         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)
7545         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)
7546         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7547
7548         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7549
7550         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7551         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)
7552         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)
7553
7554         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7555
7556         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7557         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)
7558         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7559
7560         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7561         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)
7562         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7563
7564         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7565
7566         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7567         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)
7568         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7569         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7570         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7571
7572         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7573         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)
7574         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)
7575         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7576         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7577
7578         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7579         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7580         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7581         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7582         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7583         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7584
7585         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7586         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7587         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)
7588
7589         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7590         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7591         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7592 /* phew. */
7593
7594 /* Tools menu */
7595         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7596         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7597         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7598         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7599         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7600         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7601
7602 /* Help menu */
7603         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7604
7605         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7606         gtk_widget_show_all(menubar);
7607
7608         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7609         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7610
7611         if (prefs_common.toolbar_detachable) {
7612                 handlebox = gtk_handle_box_new();
7613         } else {
7614                 handlebox = gtk_hbox_new(FALSE, 0);
7615         }
7616         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7617
7618         gtk_widget_realize(handlebox);
7619         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7620                                           (gpointer)compose);
7621
7622         vbox2 = gtk_vbox_new(FALSE, 2);
7623         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7624         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7625         
7626         /* Notebook */
7627         notebook = gtk_notebook_new();
7628         gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7629         gtk_widget_show(notebook);
7630
7631         /* header labels and entries */
7632         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7633                         compose_create_header(compose),
7634                         gtk_label_new_with_mnemonic(_("Hea_der")));
7635         /* attachment list */
7636         attach_hbox = gtk_hbox_new(FALSE, 0);
7637         gtk_widget_show(attach_hbox);
7638         
7639         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7640         gtk_widget_show(attach_lab1);
7641         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7642         
7643         attach_lab2 = gtk_label_new("");
7644         gtk_widget_show(attach_lab2);
7645         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7646         
7647         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7648                         compose_create_attach(compose),
7649                         attach_hbox);
7650         /* Others Tab */
7651         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7652                         compose_create_others(compose),
7653                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7654
7655         /* Subject */
7656         subject_hbox = gtk_hbox_new(FALSE, 0);
7657         gtk_widget_show(subject_hbox);
7658
7659         subject_frame = gtk_frame_new(NULL);
7660         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7661         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7662         gtk_widget_show(subject_frame);
7663
7664         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7665         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7666         gtk_widget_show(subject);
7667
7668         label = gtk_label_new(_("Subject:"));
7669         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7670         gtk_widget_show(label);
7671
7672 #ifdef USE_ENCHANT
7673         subject_entry = claws_spell_entry_new();
7674 #else
7675         subject_entry = gtk_entry_new();
7676 #endif
7677         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7678         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7679                          G_CALLBACK(compose_grab_focus_cb), compose);
7680         gtk_widget_show(subject_entry);
7681         compose->subject_entry = subject_entry;
7682         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7683         
7684         edit_vbox = gtk_vbox_new(FALSE, 0);
7685
7686         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7687
7688         /* ruler */
7689         ruler_hbox = gtk_hbox_new(FALSE, 0);
7690         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7691
7692         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7693         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7694         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7695                            BORDER_WIDTH);
7696
7697         /* text widget */
7698         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7699         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7700                                        GTK_POLICY_AUTOMATIC,
7701                                        GTK_POLICY_AUTOMATIC);
7702         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7703                                             GTK_SHADOW_IN);
7704         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7705
7706         text = gtk_text_view_new();
7707         if (prefs_common.show_compose_margin) {
7708                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7709                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7710         }
7711         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7712         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7713         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7714         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7715         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7716         
7717         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7718         g_signal_connect(G_OBJECT(notebook), "size_allocate",
7719                          G_CALLBACK(compose_notebook_size_alloc), compose);     
7720         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7721                                G_CALLBACK(compose_edit_size_alloc),
7722                                ruler);
7723         g_signal_connect(G_OBJECT(buffer), "changed",
7724                          G_CALLBACK(compose_changed_cb), compose);
7725         g_signal_connect(G_OBJECT(text), "grab_focus",
7726                          G_CALLBACK(compose_grab_focus_cb), compose);
7727         g_signal_connect(G_OBJECT(buffer), "insert_text",
7728                          G_CALLBACK(text_inserted), compose);
7729         g_signal_connect(G_OBJECT(text), "button_press_event",
7730                          G_CALLBACK(text_clicked), compose);
7731         g_signal_connect(G_OBJECT(text), "popup-menu",
7732                          G_CALLBACK(compose_popup_menu), compose);
7733         g_signal_connect(G_OBJECT(subject_entry), "changed",
7734                         G_CALLBACK(compose_changed_cb), compose);
7735         g_signal_connect(G_OBJECT(subject_entry), "activate",
7736                         G_CALLBACK(compose_subject_entry_activated), compose);
7737
7738         /* drag and drop */
7739         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7740                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7741                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7742         g_signal_connect(G_OBJECT(text), "drag_data_received",
7743                          G_CALLBACK(compose_insert_drag_received_cb),
7744                          compose);
7745         g_signal_connect(G_OBJECT(text), "drag-drop",
7746                          G_CALLBACK(compose_drag_drop),
7747                          compose);
7748         g_signal_connect(G_OBJECT(text), "key-press-event",
7749                          G_CALLBACK(completion_set_focus_to_subject),
7750                          compose);
7751         gtk_widget_show_all(vbox);
7752
7753         /* pane between attach clist and text */
7754         paned = gtk_vpaned_new();
7755         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7756         gtk_paned_add1(GTK_PANED(paned), notebook);
7757         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7758         gtk_widget_show_all(paned);
7759
7760
7761         if (prefs_common.textfont) {
7762                 PangoFontDescription *font_desc;
7763
7764                 font_desc = pango_font_description_from_string
7765                         (prefs_common.textfont);
7766                 if (font_desc) {
7767                         gtk_widget_modify_font(text, font_desc);
7768                         pango_font_description_free(font_desc);
7769                 }
7770         }
7771
7772         gtk_action_group_add_actions(action_group, compose_popup_entries,
7773                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7774         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7775         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7776         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7777         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7778         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7779         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7780         
7781         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7782
7783         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7784         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7785         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7786
7787         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7788
7789         undostruct = undo_init(text);
7790         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7791                                    compose);
7792
7793         address_completion_start(window);
7794
7795         compose->window        = window;
7796         compose->vbox          = vbox;
7797         compose->menubar       = menubar;
7798         compose->handlebox     = handlebox;
7799
7800         compose->vbox2         = vbox2;
7801
7802         compose->paned = paned;
7803
7804         compose->attach_label  = attach_lab2;
7805
7806         compose->notebook      = notebook;
7807         compose->edit_vbox     = edit_vbox;
7808         compose->ruler_hbox    = ruler_hbox;
7809         compose->ruler         = ruler;
7810         compose->scrolledwin   = scrolledwin;
7811         compose->text          = text;
7812
7813         compose->focused_editable = NULL;
7814
7815         compose->popupmenu    = popupmenu;
7816
7817         compose->tmpl_menu = tmpl_menu;
7818
7819         compose->mode = mode;
7820         compose->rmode = mode;
7821
7822         compose->targetinfo = NULL;
7823         compose->replyinfo  = NULL;
7824         compose->fwdinfo    = NULL;
7825
7826         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7827                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7828         
7829         compose->replyto     = NULL;
7830         compose->cc          = NULL;
7831         compose->bcc         = NULL;
7832         compose->followup_to = NULL;
7833
7834         compose->ml_post     = NULL;
7835
7836         compose->inreplyto   = NULL;
7837         compose->references  = NULL;
7838         compose->msgid       = NULL;
7839         compose->boundary    = NULL;
7840
7841         compose->autowrap       = prefs_common.autowrap;
7842         compose->autoindent     = prefs_common.auto_indent;
7843         compose->use_signing    = FALSE;
7844         compose->use_encryption = FALSE;
7845         compose->privacy_system = NULL;
7846
7847         compose->modified = FALSE;
7848
7849         compose->return_receipt = FALSE;
7850
7851         compose->to_list        = NULL;
7852         compose->newsgroup_list = NULL;
7853
7854         compose->undostruct = undostruct;
7855
7856         compose->sig_str = NULL;
7857
7858         compose->exteditor_file    = NULL;
7859         compose->exteditor_pid     = -1;
7860         compose->exteditor_tag     = -1;
7861         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7862
7863 #if USE_ENCHANT
7864         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7865         if (mode != COMPOSE_REDIRECT) {
7866                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7867                     strcmp(prefs_common.dictionary, "")) {
7868                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7869                                                   prefs_common.alt_dictionary,
7870                                                   conv_get_locale_charset_str(),
7871                                                   prefs_common.misspelled_col,
7872                                                   prefs_common.check_while_typing,
7873                                                   prefs_common.recheck_when_changing_dict,
7874                                                   prefs_common.use_alternate,
7875                                                   prefs_common.use_both_dicts,
7876                                                   GTK_TEXT_VIEW(text),
7877                                                   GTK_WINDOW(compose->window),
7878                                                   compose_dict_changed,
7879                                                   compose_spell_menu_changed,
7880                                                   compose);
7881                         if (!gtkaspell) {
7882                                 alertpanel_error(_("Spell checker could not "
7883                                                 "be started.\n%s"),
7884                                                 gtkaspell_checkers_strerror());
7885                                 gtkaspell_checkers_reset_error();
7886                         } else {
7887                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7888                         }
7889                 }
7890         }
7891         compose->gtkaspell = gtkaspell;
7892         compose_spell_menu_changed(compose);
7893         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7894 #endif
7895
7896         compose_select_account(compose, account, TRUE);
7897
7898         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7899         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7900
7901         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7902                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7903
7904         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7905                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7906         
7907         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7908                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7909
7910         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7911         if (account->protocol != A_NNTP)
7912                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7913                                 prefs_common_translated_header_name("To:"));
7914         else
7915                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7916                                 prefs_common_translated_header_name("Newsgroups:"));
7917
7918 #ifndef USE_NEW_ADDRBOOK
7919         addressbook_set_target_compose(compose);
7920 #endif  
7921         if (mode != COMPOSE_REDIRECT)
7922                 compose_set_template_menu(compose);
7923         else {
7924                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7925         }
7926
7927         compose_list = g_list_append(compose_list, compose);
7928
7929         if (!prefs_common.show_ruler)
7930                 gtk_widget_hide(ruler_hbox);
7931                 
7932         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7933
7934         /* Priority */
7935         compose->priority = PRIORITY_NORMAL;
7936         compose_update_priority_menu_item(compose);
7937
7938         compose_set_out_encoding(compose);
7939         
7940         /* Actions menu */
7941         compose_update_actions_menu(compose);
7942
7943         /* Privacy Systems menu */
7944         compose_update_privacy_systems_menu(compose);
7945
7946         activate_privacy_system(compose, account, TRUE);
7947         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7948         if (batch) {
7949                 gtk_widget_realize(window);
7950         } else {
7951                 gtk_widget_show(window);
7952         }
7953         
7954         return compose;
7955 }
7956
7957 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7958 {
7959         GList *accounts;
7960         GtkWidget *hbox;
7961         GtkWidget *optmenu;
7962         GtkWidget *optmenubox;
7963         GtkListStore *menu;
7964         GtkTreeIter iter;
7965         GtkWidget *from_name = NULL;
7966 #if !(GTK_CHECK_VERSION(2,12,0))
7967         GtkTooltips *tips = compose->tooltips;
7968 #endif
7969
7970         gint num = 0, def_menu = 0;
7971         
7972         accounts = account_get_list();
7973         cm_return_val_if_fail(accounts != NULL, NULL);
7974
7975         optmenubox = gtk_event_box_new();
7976         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7977         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7978
7979         hbox = gtk_hbox_new(FALSE, 6);
7980         from_name = gtk_entry_new();
7981         
7982         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7983                          G_CALLBACK(compose_grab_focus_cb), compose);
7984
7985         for (; accounts != NULL; accounts = accounts->next, num++) {
7986                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7987                 gchar *name, *from = NULL;
7988
7989                 if (ac == compose->account) def_menu = num;
7990
7991                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7992                                        ac->account_name);
7993                 
7994                 if (ac == compose->account) {
7995                         if (ac->name && *ac->name) {
7996                                 gchar *buf;
7997                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7998                                 from = g_strdup_printf("%s <%s>",
7999                                                        buf, ac->address);
8000                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8001                         } else {
8002                                 from = g_strdup_printf("%s",
8003                                                        ac->address);
8004                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8005                         }
8006                 }
8007                 COMBOBOX_ADD(menu, name, ac->account_id);
8008                 g_free(name);
8009                 g_free(from);
8010         }
8011
8012         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8013
8014         g_signal_connect(G_OBJECT(optmenu), "changed",
8015                         G_CALLBACK(account_activated),
8016                         compose);
8017         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8018                          G_CALLBACK(compose_entry_popup_extend),
8019                          NULL);
8020
8021         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8022         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8023         
8024         CLAWS_SET_TIP(optmenubox,
8025                 _("Account to use for this email"));
8026         CLAWS_SET_TIP(from_name,
8027                 _("Sender address to be used"));
8028
8029         compose->account_combo = optmenu;
8030         compose->from_name = from_name;
8031         
8032         return hbox;
8033 }
8034
8035 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8036 {
8037         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8038         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8039         Compose *compose = (Compose *) data;
8040         if (active) {
8041                 compose->priority = value;
8042         }
8043 }
8044
8045 static void compose_reply_change_mode(Compose *compose,
8046                                     ComposeMode action)
8047 {
8048         gboolean was_modified = compose->modified;
8049
8050         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8051         
8052         cm_return_if_fail(compose->replyinfo != NULL);
8053         
8054         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8055                 ml = TRUE;
8056         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8057                 followup = TRUE;
8058         if (action == COMPOSE_REPLY_TO_ALL)
8059                 all = TRUE;
8060         if (action == COMPOSE_REPLY_TO_SENDER)
8061                 sender = TRUE;
8062         if (action == COMPOSE_REPLY_TO_LIST)
8063                 ml = TRUE;
8064
8065         compose_remove_header_entries(compose);
8066         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8067         if (compose->account->set_autocc && compose->account->auto_cc)
8068                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8069
8070         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8071                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8072         
8073         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8074                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8075         compose_show_first_last_header(compose, TRUE);
8076         compose->modified = was_modified;
8077         compose_set_title(compose);
8078 }
8079
8080 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8081 {
8082         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8083         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8084         Compose *compose = (Compose *) data;
8085         
8086         if (active)
8087                 compose_reply_change_mode(compose, value);
8088 }
8089
8090 static void compose_update_priority_menu_item(Compose * compose)
8091 {
8092         GtkWidget *menuitem = NULL;
8093         switch (compose->priority) {
8094                 case PRIORITY_HIGHEST:
8095                         menuitem = gtk_ui_manager_get_widget
8096                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8097                         break;
8098                 case PRIORITY_HIGH:
8099                         menuitem = gtk_ui_manager_get_widget
8100                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8101                         break;
8102                 case PRIORITY_NORMAL:
8103                         menuitem = gtk_ui_manager_get_widget
8104                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8105                         break;
8106                 case PRIORITY_LOW:
8107                         menuitem = gtk_ui_manager_get_widget
8108                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8109                         break;
8110                 case PRIORITY_LOWEST:
8111                         menuitem = gtk_ui_manager_get_widget
8112                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8113                         break;
8114         }
8115         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8116 }       
8117
8118 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8119 {
8120         Compose *compose = (Compose *) data;
8121         gchar *systemid;
8122         gboolean can_sign = FALSE, can_encrypt = FALSE;
8123
8124         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8125
8126         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8127                 return;
8128
8129         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8130         g_free(compose->privacy_system);
8131         compose->privacy_system = NULL;
8132         if (systemid != NULL) {
8133                 compose->privacy_system = g_strdup(systemid);
8134
8135                 can_sign = privacy_system_can_sign(systemid);
8136                 can_encrypt = privacy_system_can_encrypt(systemid);
8137         }
8138
8139         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8140
8141         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8142         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8143 }
8144
8145 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8146 {
8147         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8148         GtkWidget *menuitem = NULL;
8149         GList *children, *amenu;
8150         gboolean can_sign = FALSE, can_encrypt = FALSE;
8151         gboolean found = FALSE;
8152
8153         if (compose->privacy_system != NULL) {
8154                 gchar *systemid;
8155                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8156                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8157                 cm_return_if_fail(menuitem != NULL);
8158
8159                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8160                 amenu = children;
8161                 menuitem = NULL;
8162                 while (amenu != NULL) {
8163                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8164                         if (systemid != NULL) {
8165                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8166                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8167                                         menuitem = GTK_WIDGET(amenu->data);
8168
8169                                         can_sign = privacy_system_can_sign(systemid);
8170                                         can_encrypt = privacy_system_can_encrypt(systemid);
8171                                         found = TRUE;
8172                                         break;
8173                                 } 
8174                         } else if (strlen(compose->privacy_system) == 0 && 
8175                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8176                                         menuitem = GTK_WIDGET(amenu->data);
8177
8178                                         can_sign = FALSE;
8179                                         can_encrypt = FALSE;
8180                                         found = TRUE;
8181                                         break;
8182                         }
8183
8184                         amenu = amenu->next;
8185                 }
8186                 g_list_free(children);
8187                 if (menuitem != NULL)
8188                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8189                 
8190                 if (warn && !found && strlen(compose->privacy_system)) {
8191                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8192                                   "will not be able to sign or encrypt this message."),
8193                                   compose->privacy_system);
8194                 }
8195         } 
8196
8197         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8198         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8199 }       
8200  
8201 static void compose_set_out_encoding(Compose *compose)
8202 {
8203         CharSet out_encoding;
8204         const gchar *branch = NULL;
8205         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8206
8207         switch(out_encoding) {
8208                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8209                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8210                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8211                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8212                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8213                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8214                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8215                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8216                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8217                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8218                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8219                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8220                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8221                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8222                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8223                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8224                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8225                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8226                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8227                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8228                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8229                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8230                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8231                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8232                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8233                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8234                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8235                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8236                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8237                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8238                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8239                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8240                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8241         }
8242         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8243 }
8244
8245 static void compose_set_template_menu(Compose *compose)
8246 {
8247         GSList *tmpl_list, *cur;
8248         GtkWidget *menu;
8249         GtkWidget *item;
8250
8251         tmpl_list = template_get_config();
8252
8253         menu = gtk_menu_new();
8254
8255         gtk_menu_set_accel_group (GTK_MENU (menu), 
8256                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8257         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8258                 Template *tmpl = (Template *)cur->data;
8259                 gchar *accel_path = NULL;
8260                 item = gtk_menu_item_new_with_label(tmpl->name);
8261                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8262                 g_signal_connect(G_OBJECT(item), "activate",
8263                                  G_CALLBACK(compose_template_activate_cb),
8264                                  compose);
8265                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8266                 gtk_widget_show(item);
8267                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8268                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8269                 g_free(accel_path);
8270         }
8271
8272         gtk_widget_show(menu);
8273         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8274 }
8275
8276 void compose_update_actions_menu(Compose *compose)
8277 {
8278         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8279 }
8280
8281 static void compose_update_privacy_systems_menu(Compose *compose)
8282 {
8283         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8284         GSList *systems, *cur;
8285         GtkWidget *widget;
8286         GtkWidget *system_none;
8287         GSList *group;
8288         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8289         GtkWidget *privacy_menu = gtk_menu_new();
8290
8291         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8292         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8293
8294         g_signal_connect(G_OBJECT(system_none), "activate",
8295                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8296
8297         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8298         gtk_widget_show(system_none);
8299
8300         systems = privacy_get_system_ids();
8301         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8302                 gchar *systemid = cur->data;
8303
8304                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8305                 widget = gtk_radio_menu_item_new_with_label(group,
8306                         privacy_system_get_name(systemid));
8307                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8308                                        g_strdup(systemid), g_free);
8309                 g_signal_connect(G_OBJECT(widget), "activate",
8310                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8311
8312                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8313                 gtk_widget_show(widget);
8314                 g_free(systemid);
8315         }
8316         g_slist_free(systems);
8317         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8318         gtk_widget_show_all(privacy_menu);
8319         gtk_widget_show_all(privacy_menuitem);
8320 }
8321
8322 void compose_reflect_prefs_all(void)
8323 {
8324         GList *cur;
8325         Compose *compose;
8326
8327         for (cur = compose_list; cur != NULL; cur = cur->next) {
8328                 compose = (Compose *)cur->data;
8329                 compose_set_template_menu(compose);
8330         }
8331 }
8332
8333 void compose_reflect_prefs_pixmap_theme(void)
8334 {
8335         GList *cur;
8336         Compose *compose;
8337
8338         for (cur = compose_list; cur != NULL; cur = cur->next) {
8339                 compose = (Compose *)cur->data;
8340                 toolbar_update(TOOLBAR_COMPOSE, compose);
8341         }
8342 }
8343
8344 static const gchar *compose_quote_char_from_context(Compose *compose)
8345 {
8346         const gchar *qmark = NULL;
8347
8348         cm_return_val_if_fail(compose != NULL, NULL);
8349
8350         switch (compose->mode) {
8351                 /* use forward-specific quote char */
8352                 case COMPOSE_FORWARD:
8353                 case COMPOSE_FORWARD_AS_ATTACH:
8354                 case COMPOSE_FORWARD_INLINE:
8355                         if (compose->folder && compose->folder->prefs &&
8356                                         compose->folder->prefs->forward_with_format)
8357                                 qmark = compose->folder->prefs->forward_quotemark;
8358                         else if (compose->account->forward_with_format)
8359                                 qmark = compose->account->forward_quotemark;
8360                         else
8361                                 qmark = prefs_common.fw_quotemark;
8362                         break;
8363
8364                 /* use reply-specific quote char in all other modes */
8365                 default:
8366                         if (compose->folder && compose->folder->prefs &&
8367                                         compose->folder->prefs->reply_with_format)
8368                                 qmark = compose->folder->prefs->reply_quotemark;
8369                         else if (compose->account->reply_with_format)
8370                                 qmark = compose->account->reply_quotemark;
8371                         else
8372                                 qmark = prefs_common.quotemark;
8373                         break;
8374         }
8375
8376         if (qmark == NULL || *qmark == '\0')
8377                 qmark = "> ";
8378
8379         return qmark;
8380 }
8381
8382 static void compose_template_apply(Compose *compose, Template *tmpl,
8383                                    gboolean replace)
8384 {
8385         GtkTextView *text;
8386         GtkTextBuffer *buffer;
8387         GtkTextMark *mark;
8388         GtkTextIter iter;
8389         const gchar *qmark;
8390         gchar *parsed_str = NULL;
8391         gint cursor_pos = 0;
8392         const gchar *err_msg = _("The body of the template has an error at line %d.");
8393         if (!tmpl) return;
8394
8395         /* process the body */
8396
8397         text = GTK_TEXT_VIEW(compose->text);
8398         buffer = gtk_text_view_get_buffer(text);
8399
8400         if (tmpl->value) {
8401                 qmark = compose_quote_char_from_context(compose);
8402
8403                 if (compose->replyinfo != NULL) {
8404
8405                         if (replace)
8406                                 gtk_text_buffer_set_text(buffer, "", -1);
8407                         mark = gtk_text_buffer_get_insert(buffer);
8408                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8409
8410                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8411                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8412
8413                 } else if (compose->fwdinfo != NULL) {
8414
8415                         if (replace)
8416                                 gtk_text_buffer_set_text(buffer, "", -1);
8417                         mark = gtk_text_buffer_get_insert(buffer);
8418                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8419
8420                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8421                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8422
8423                 } else {
8424                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8425
8426                         GtkTextIter start, end;
8427                         gchar *tmp = NULL;
8428
8429                         gtk_text_buffer_get_start_iter(buffer, &start);
8430                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8431                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8432
8433                         /* clear the buffer now */
8434                         if (replace)
8435                                 gtk_text_buffer_set_text(buffer, "", -1);
8436
8437                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8438                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8439                         procmsg_msginfo_free( dummyinfo );
8440
8441                         g_free( tmp );
8442                 } 
8443         } else {
8444                 if (replace)
8445                         gtk_text_buffer_set_text(buffer, "", -1);
8446                 mark = gtk_text_buffer_get_insert(buffer);
8447                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8448         }       
8449
8450         if (replace && parsed_str && compose->account->auto_sig)
8451                 compose_insert_sig(compose, FALSE);
8452
8453         if (replace && parsed_str) {
8454                 gtk_text_buffer_get_start_iter(buffer, &iter);
8455                 gtk_text_buffer_place_cursor(buffer, &iter);
8456         }
8457         
8458         if (parsed_str) {
8459                 cursor_pos = quote_fmt_get_cursor_pos();
8460                 compose->set_cursor_pos = cursor_pos;
8461                 if (cursor_pos == -1)
8462                         cursor_pos = 0;
8463                 gtk_text_buffer_get_start_iter(buffer, &iter);
8464                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8465                 gtk_text_buffer_place_cursor(buffer, &iter);
8466         }
8467
8468         /* process the other fields */
8469
8470         compose_template_apply_fields(compose, tmpl);
8471         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8472         quote_fmt_reset_vartable();
8473         compose_changed_cb(NULL, compose);
8474
8475 #ifdef USE_ENCHANT
8476         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8477                 gtkaspell_highlight_all(compose->gtkaspell);
8478 #endif
8479 }
8480
8481 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8482 {
8483         MsgInfo* dummyinfo = NULL;
8484         MsgInfo *msginfo = NULL;
8485         gchar *buf = NULL;
8486
8487         if (compose->replyinfo != NULL)
8488                 msginfo = compose->replyinfo;
8489         else if (compose->fwdinfo != NULL)
8490                 msginfo = compose->fwdinfo;
8491         else {
8492                 dummyinfo = compose_msginfo_new_from_compose(compose);
8493                 msginfo = dummyinfo;
8494         }
8495
8496         if (tmpl->from && *tmpl->from != '\0') {
8497 #ifdef USE_ENCHANT
8498                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8499                                 compose->gtkaspell);
8500 #else
8501                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8502 #endif
8503                 quote_fmt_scan_string(tmpl->from);
8504                 quote_fmt_parse();
8505
8506                 buf = quote_fmt_get_buffer();
8507                 if (buf == NULL) {
8508                         alertpanel_error(_("Template From format error."));
8509                 } else {
8510                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8511                 }
8512         }
8513
8514         if (tmpl->to && *tmpl->to != '\0') {
8515 #ifdef USE_ENCHANT
8516                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8517                                 compose->gtkaspell);
8518 #else
8519                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8520 #endif
8521                 quote_fmt_scan_string(tmpl->to);
8522                 quote_fmt_parse();
8523
8524                 buf = quote_fmt_get_buffer();
8525                 if (buf == NULL) {
8526                         alertpanel_error(_("Template To format error."));
8527                 } else {
8528                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8529                 }
8530         }
8531
8532         if (tmpl->cc && *tmpl->cc != '\0') {
8533 #ifdef USE_ENCHANT
8534                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8535                                 compose->gtkaspell);
8536 #else
8537                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8538 #endif
8539                 quote_fmt_scan_string(tmpl->cc);
8540                 quote_fmt_parse();
8541
8542                 buf = quote_fmt_get_buffer();
8543                 if (buf == NULL) {
8544                         alertpanel_error(_("Template Cc format error."));
8545                 } else {
8546                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8547                 }
8548         }
8549
8550         if (tmpl->bcc && *tmpl->bcc != '\0') {
8551 #ifdef USE_ENCHANT
8552                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8553                                 compose->gtkaspell);
8554 #else
8555                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8556 #endif
8557                 quote_fmt_scan_string(tmpl->bcc);
8558                 quote_fmt_parse();
8559
8560                 buf = quote_fmt_get_buffer();
8561                 if (buf == NULL) {
8562                         alertpanel_error(_("Template Bcc format error."));
8563                 } else {
8564                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8565                 }
8566         }
8567
8568         /* process the subject */
8569         if (tmpl->subject && *tmpl->subject != '\0') {
8570 #ifdef USE_ENCHANT
8571                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8572                                 compose->gtkaspell);
8573 #else
8574                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8575 #endif
8576                 quote_fmt_scan_string(tmpl->subject);
8577                 quote_fmt_parse();
8578
8579                 buf = quote_fmt_get_buffer();
8580                 if (buf == NULL) {
8581                         alertpanel_error(_("Template subject format error."));
8582                 } else {
8583                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8584                 }
8585         }
8586
8587         procmsg_msginfo_free( dummyinfo );
8588 }
8589
8590 static void compose_destroy(Compose *compose)
8591 {
8592         GtkAllocation allocation;
8593         GtkTextBuffer *buffer;
8594         GtkClipboard *clipboard;
8595
8596         compose_list = g_list_remove(compose_list, compose);
8597
8598         if (compose->updating) {
8599                 debug_print("danger, not destroying anything now\n");
8600                 compose->deferred_destroy = TRUE;
8601                 return;
8602         }
8603         /* NOTE: address_completion_end() does nothing with the window
8604          * however this may change. */
8605         address_completion_end(compose->window);
8606
8607         slist_free_strings_full(compose->to_list);
8608         slist_free_strings_full(compose->newsgroup_list);
8609         slist_free_strings_full(compose->header_list);
8610
8611         slist_free_strings_full(extra_headers);
8612         extra_headers = NULL;
8613
8614         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8615
8616         g_hash_table_destroy(compose->email_hashtable);
8617
8618         procmsg_msginfo_free(compose->targetinfo);
8619         procmsg_msginfo_free(compose->replyinfo);
8620         procmsg_msginfo_free(compose->fwdinfo);
8621
8622         g_free(compose->replyto);
8623         g_free(compose->cc);
8624         g_free(compose->bcc);
8625         g_free(compose->newsgroups);
8626         g_free(compose->followup_to);
8627
8628         g_free(compose->ml_post);
8629
8630         g_free(compose->inreplyto);
8631         g_free(compose->references);
8632         g_free(compose->msgid);
8633         g_free(compose->boundary);
8634
8635         g_free(compose->redirect_filename);
8636         if (compose->undostruct)
8637                 undo_destroy(compose->undostruct);
8638
8639         g_free(compose->sig_str);
8640
8641         g_free(compose->exteditor_file);
8642
8643         g_free(compose->orig_charset);
8644
8645         g_free(compose->privacy_system);
8646
8647 #ifndef USE_NEW_ADDRBOOK
8648         if (addressbook_get_target_compose() == compose)
8649                 addressbook_set_target_compose(NULL);
8650 #endif
8651 #if USE_ENCHANT
8652         if (compose->gtkaspell) {
8653                 gtkaspell_delete(compose->gtkaspell);
8654                 compose->gtkaspell = NULL;
8655         }
8656 #endif
8657
8658         if (!compose->batch) {
8659                 gtk_widget_get_allocation(compose->window, &allocation);
8660                 prefs_common.compose_width = allocation.width;
8661                 prefs_common.compose_height = allocation.height;
8662         }
8663
8664         if (!gtk_widget_get_parent(compose->paned))
8665                 gtk_widget_destroy(compose->paned);
8666         gtk_widget_destroy(compose->popupmenu);
8667
8668         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8669         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8670         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8671
8672         gtk_widget_destroy(compose->window);
8673         toolbar_destroy(compose->toolbar);
8674         g_free(compose->toolbar);
8675         cm_mutex_free(compose->mutex);
8676         g_free(compose);
8677 }
8678
8679 static void compose_attach_info_free(AttachInfo *ainfo)
8680 {
8681         g_free(ainfo->file);
8682         g_free(ainfo->content_type);
8683         g_free(ainfo->name);
8684         g_free(ainfo->charset);
8685         g_free(ainfo);
8686 }
8687
8688 static void compose_attach_update_label(Compose *compose)
8689 {
8690         GtkTreeIter iter;
8691         gint i = 1;
8692         gchar *text;
8693         GtkTreeModel *model;
8694         
8695         if(compose == NULL)
8696                 return;
8697                 
8698         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8699         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8700                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8701                 return;
8702         }
8703         
8704         while(gtk_tree_model_iter_next(model, &iter))
8705                 i++;
8706         
8707         text = g_strdup_printf("(%d)", i);
8708         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8709         g_free(text);
8710 }
8711
8712 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8713 {
8714         Compose *compose = (Compose *)data;
8715         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8716         GtkTreeSelection *selection;
8717         GList *sel, *cur;
8718         GtkTreeModel *model;
8719
8720         selection = gtk_tree_view_get_selection(tree_view);
8721         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8722
8723         if (!sel) 
8724                 return;
8725
8726         for (cur = sel; cur != NULL; cur = cur->next) {
8727                 GtkTreePath *path = cur->data;
8728                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8729                                                 (model, cur->data);
8730                 cur->data = ref;
8731                 gtk_tree_path_free(path);
8732         }
8733
8734         for (cur = sel; cur != NULL; cur = cur->next) {
8735                 GtkTreeRowReference *ref = cur->data;
8736                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8737                 GtkTreeIter iter;
8738
8739                 if (gtk_tree_model_get_iter(model, &iter, path))
8740                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8741                 
8742                 gtk_tree_path_free(path);
8743                 gtk_tree_row_reference_free(ref);
8744         }
8745
8746         g_list_free(sel);
8747         compose_attach_update_label(compose);
8748 }
8749
8750 static struct _AttachProperty
8751 {
8752         GtkWidget *window;
8753         GtkWidget *mimetype_entry;
8754         GtkWidget *encoding_optmenu;
8755         GtkWidget *path_entry;
8756         GtkWidget *filename_entry;
8757         GtkWidget *ok_btn;
8758         GtkWidget *cancel_btn;
8759 } attach_prop;
8760
8761 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8762 {       
8763         gtk_tree_path_free((GtkTreePath *)ptr);
8764 }
8765
8766 static void compose_attach_property(GtkAction *action, gpointer data)
8767 {
8768         Compose *compose = (Compose *)data;
8769         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8770         AttachInfo *ainfo;
8771         GtkComboBox *optmenu;
8772         GtkTreeSelection *selection;
8773         GList *sel;
8774         GtkTreeModel *model;
8775         GtkTreeIter iter;
8776         GtkTreePath *path;
8777         static gboolean cancelled;
8778
8779         /* only if one selected */
8780         selection = gtk_tree_view_get_selection(tree_view);
8781         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8782                 return;
8783
8784         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8785         if (!sel)
8786                 return;
8787
8788         path = (GtkTreePath *) sel->data;
8789         gtk_tree_model_get_iter(model, &iter, path);
8790         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8791         
8792         if (!ainfo) {
8793                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8794                 g_list_free(sel);
8795                 return;
8796         }               
8797         g_list_free(sel);
8798
8799         if (!attach_prop.window)
8800                 compose_attach_property_create(&cancelled);
8801         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8802         gtk_widget_grab_focus(attach_prop.ok_btn);
8803         gtk_widget_show(attach_prop.window);
8804         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8805                         GTK_WINDOW(compose->window));
8806
8807         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8808         if (ainfo->encoding == ENC_UNKNOWN)
8809                 combobox_select_by_data(optmenu, ENC_BASE64);
8810         else
8811                 combobox_select_by_data(optmenu, ainfo->encoding);
8812
8813         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8814                            ainfo->content_type ? ainfo->content_type : "");
8815         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8816                            ainfo->file ? ainfo->file : "");
8817         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8818                            ainfo->name ? ainfo->name : "");
8819
8820         for (;;) {
8821                 const gchar *entry_text;
8822                 gchar *text;
8823                 gchar *cnttype = NULL;
8824                 gchar *file = NULL;
8825                 off_t size = 0;
8826
8827                 cancelled = FALSE;
8828                 gtk_main();
8829
8830                 gtk_widget_hide(attach_prop.window);
8831                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8832                 
8833                 if (cancelled) 
8834                         break;
8835
8836                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8837                 if (*entry_text != '\0') {
8838                         gchar *p;
8839
8840                         text = g_strstrip(g_strdup(entry_text));
8841                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8842                                 cnttype = g_strdup(text);
8843                                 g_free(text);
8844                         } else {
8845                                 alertpanel_error(_("Invalid MIME type."));
8846                                 g_free(text);
8847                                 continue;
8848                         }
8849                 }
8850
8851                 ainfo->encoding = combobox_get_active_data(optmenu);
8852
8853                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8854                 if (*entry_text != '\0') {
8855                         if (is_file_exist(entry_text) &&
8856                             (size = get_file_size(entry_text)) > 0)
8857                                 file = g_strdup(entry_text);
8858                         else {
8859                                 alertpanel_error
8860                                         (_("File doesn't exist or is empty."));
8861                                 g_free(cnttype);
8862                                 continue;
8863                         }
8864                 }
8865
8866                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8867                 if (*entry_text != '\0') {
8868                         g_free(ainfo->name);
8869                         ainfo->name = g_strdup(entry_text);
8870                 }
8871
8872                 if (cnttype) {
8873                         g_free(ainfo->content_type);
8874                         ainfo->content_type = cnttype;
8875                 }
8876                 if (file) {
8877                         g_free(ainfo->file);
8878                         ainfo->file = file;
8879                 }
8880                 if (size)
8881                         ainfo->size = (goffset)size;
8882
8883                 /* update tree store */
8884                 text = to_human_readable(ainfo->size);
8885                 gtk_tree_model_get_iter(model, &iter, path);
8886                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8887                                    COL_MIMETYPE, ainfo->content_type,
8888                                    COL_SIZE, text,
8889                                    COL_NAME, ainfo->name,
8890                                    COL_CHARSET, ainfo->charset,
8891                                    -1);
8892                 
8893                 break;
8894         }
8895
8896         gtk_tree_path_free(path);
8897 }
8898
8899 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8900 { \
8901         label = gtk_label_new(str); \
8902         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8903                          GTK_FILL, 0, 0, 0); \
8904         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8905  \
8906         entry = gtk_entry_new(); \
8907         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8908                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8909 }
8910
8911 static void compose_attach_property_create(gboolean *cancelled)
8912 {
8913         GtkWidget *window;
8914         GtkWidget *vbox;
8915         GtkWidget *table;
8916         GtkWidget *label;
8917         GtkWidget *mimetype_entry;
8918         GtkWidget *hbox;
8919         GtkWidget *optmenu;
8920         GtkListStore *optmenu_menu;
8921         GtkWidget *path_entry;
8922         GtkWidget *filename_entry;
8923         GtkWidget *hbbox;
8924         GtkWidget *ok_btn;
8925         GtkWidget *cancel_btn;
8926         GList     *mime_type_list, *strlist;
8927         GtkTreeIter iter;
8928
8929         debug_print("Creating attach_property window...\n");
8930
8931         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8932         gtk_widget_set_size_request(window, 480, -1);
8933         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8934         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8935         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8936         g_signal_connect(G_OBJECT(window), "delete_event",
8937                          G_CALLBACK(attach_property_delete_event),
8938                          cancelled);
8939         g_signal_connect(G_OBJECT(window), "key_press_event",
8940                          G_CALLBACK(attach_property_key_pressed),
8941                          cancelled);
8942
8943         vbox = gtk_vbox_new(FALSE, 8);
8944         gtk_container_add(GTK_CONTAINER(window), vbox);
8945
8946         table = gtk_table_new(4, 2, FALSE);
8947         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8948         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8949         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8950
8951         label = gtk_label_new(_("MIME type")); 
8952         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8953                          GTK_FILL, 0, 0, 0); 
8954         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8955 #if !GTK_CHECK_VERSION(2, 24, 0)
8956         mimetype_entry = gtk_combo_box_entry_new_text(); 
8957 #else
8958         mimetype_entry = gtk_combo_box_text_new_with_entry();
8959 #endif
8960         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8961                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8962                          
8963         /* stuff with list */
8964         mime_type_list = procmime_get_mime_type_list();
8965         strlist = NULL;
8966         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8967                 MimeType *type = (MimeType *) mime_type_list->data;
8968                 gchar *tmp;
8969
8970                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8971
8972                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8973                         g_free(tmp);
8974                 else
8975                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8976                                         (GCompareFunc)strcmp2);
8977         }
8978
8979         for (mime_type_list = strlist; mime_type_list != NULL; 
8980                 mime_type_list = mime_type_list->next) {
8981 #if !GTK_CHECK_VERSION(2, 24, 0)
8982                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8983 #else
8984                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
8985 #endif
8986                 g_free(mime_type_list->data);
8987         }
8988         g_list_free(strlist);
8989         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8990         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8991
8992         label = gtk_label_new(_("Encoding"));
8993         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8994                          GTK_FILL, 0, 0, 0);
8995         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8996
8997         hbox = gtk_hbox_new(FALSE, 0);
8998         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8999                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9000
9001         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9002         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9003
9004         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9005         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9006         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9007         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9008         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9009
9010         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9011
9012         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9013         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9014
9015         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9016                                       &ok_btn, GTK_STOCK_OK,
9017                                       NULL, NULL);
9018         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9019         gtk_widget_grab_default(ok_btn);
9020
9021         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9022                          G_CALLBACK(attach_property_ok),
9023                          cancelled);
9024         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9025                          G_CALLBACK(attach_property_cancel),
9026                          cancelled);
9027
9028         gtk_widget_show_all(vbox);
9029
9030         attach_prop.window           = window;
9031         attach_prop.mimetype_entry   = mimetype_entry;
9032         attach_prop.encoding_optmenu = optmenu;
9033         attach_prop.path_entry       = path_entry;
9034         attach_prop.filename_entry   = filename_entry;
9035         attach_prop.ok_btn           = ok_btn;
9036         attach_prop.cancel_btn       = cancel_btn;
9037 }
9038
9039 #undef SET_LABEL_AND_ENTRY
9040
9041 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9042 {
9043         *cancelled = FALSE;
9044         gtk_main_quit();
9045 }
9046
9047 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9048 {
9049         *cancelled = TRUE;
9050         gtk_main_quit();
9051 }
9052
9053 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9054                                          gboolean *cancelled)
9055 {
9056         *cancelled = TRUE;
9057         gtk_main_quit();
9058
9059         return TRUE;
9060 }
9061
9062 static gboolean attach_property_key_pressed(GtkWidget *widget,
9063                                             GdkEventKey *event,
9064                                             gboolean *cancelled)
9065 {
9066         if (event && event->keyval == GDK_KEY_Escape) {
9067                 *cancelled = TRUE;
9068                 gtk_main_quit();
9069         }
9070         if (event && event->keyval == GDK_KEY_Return) {
9071                 *cancelled = FALSE;
9072                 gtk_main_quit();
9073                 return TRUE;
9074         }
9075         return FALSE;
9076 }
9077
9078 static void compose_exec_ext_editor(Compose *compose)
9079 {
9080 #ifdef G_OS_UNIX
9081         gchar *tmp;
9082         pid_t pid;
9083         gint pipe_fds[2];
9084
9085         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9086                               G_DIR_SEPARATOR, compose);
9087
9088         if (pipe(pipe_fds) < 0) {
9089                 perror("pipe");
9090                 g_free(tmp);
9091                 return;
9092         }
9093
9094         if ((pid = fork()) < 0) {
9095                 perror("fork");
9096                 g_free(tmp);
9097                 return;
9098         }
9099
9100         if (pid != 0) {
9101                 /* close the write side of the pipe */
9102                 close(pipe_fds[1]);
9103
9104                 compose->exteditor_file    = g_strdup(tmp);
9105                 compose->exteditor_pid     = pid;
9106
9107                 compose_set_ext_editor_sensitive(compose, FALSE);
9108
9109 #ifndef G_OS_WIN32
9110                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9111 #else
9112                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9113 #endif
9114                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9115                                                         G_IO_IN,
9116                                                         compose_input_cb,
9117                                                         compose);
9118         } else {        /* process-monitoring process */
9119                 pid_t pid_ed;
9120
9121                 if (setpgid(0, 0))
9122                         perror("setpgid");
9123
9124                 /* close the read side of the pipe */
9125                 close(pipe_fds[0]);
9126
9127                 if (compose_write_body_to_file(compose, tmp) < 0) {
9128                         fd_write_all(pipe_fds[1], "2\n", 2);
9129                         _exit(1);
9130                 }
9131
9132                 pid_ed = compose_exec_ext_editor_real(tmp);
9133                 if (pid_ed < 0) {
9134                         fd_write_all(pipe_fds[1], "1\n", 2);
9135                         _exit(1);
9136                 }
9137
9138                 /* wait until editor is terminated */
9139                 waitpid(pid_ed, NULL, 0);
9140
9141                 fd_write_all(pipe_fds[1], "0\n", 2);
9142
9143                 close(pipe_fds[1]);
9144                 _exit(0);
9145         }
9146
9147         g_free(tmp);
9148 #endif /* G_OS_UNIX */
9149 }
9150
9151 #ifdef G_OS_UNIX
9152 static gint compose_exec_ext_editor_real(const gchar *file)
9153 {
9154         gchar buf[1024];
9155         gchar *p;
9156         gchar **cmdline;
9157         pid_t pid;
9158
9159         cm_return_val_if_fail(file != NULL, -1);
9160
9161         if ((pid = fork()) < 0) {
9162                 perror("fork");
9163                 return -1;
9164         }
9165
9166         if (pid != 0) return pid;
9167
9168         /* grandchild process */
9169
9170         if (setpgid(0, getppid()))
9171                 perror("setpgid");
9172
9173         if (prefs_common_get_ext_editor_cmd() &&
9174             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9175             *(p + 1) == 's' && !strchr(p + 2, '%')) {
9176                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9177         } else {
9178                 if (prefs_common_get_ext_editor_cmd())
9179                         g_warning("External editor command-line is invalid: '%s'\n",
9180                                   prefs_common_get_ext_editor_cmd());
9181                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9182         }
9183
9184         cmdline = strsplit_with_quote(buf, " ", 1024);
9185         execvp(cmdline[0], cmdline);
9186
9187         perror("execvp");
9188         g_strfreev(cmdline);
9189
9190         _exit(1);
9191 }
9192
9193 static gboolean compose_ext_editor_kill(Compose *compose)
9194 {
9195         pid_t pgid = compose->exteditor_pid * -1;
9196         gint ret;
9197
9198         ret = kill(pgid, 0);
9199
9200         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9201                 AlertValue val;
9202                 gchar *msg;
9203
9204                 msg = g_strdup_printf
9205                         (_("The external editor is still working.\n"
9206                            "Force terminating the process?\n"
9207                            "process group id: %d"), -pgid);
9208                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9209                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9210                         
9211                 g_free(msg);
9212
9213                 if (val == G_ALERTALTERNATE) {
9214                         g_source_remove(compose->exteditor_tag);
9215                         g_io_channel_shutdown(compose->exteditor_ch,
9216                                               FALSE, NULL);
9217                         g_io_channel_unref(compose->exteditor_ch);
9218
9219                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9220                         waitpid(compose->exteditor_pid, NULL, 0);
9221
9222                         g_warning("Terminated process group id: %d", -pgid);
9223                         g_warning("Temporary file: %s",
9224                                   compose->exteditor_file);
9225
9226                         compose_set_ext_editor_sensitive(compose, TRUE);
9227
9228                         g_free(compose->exteditor_file);
9229                         compose->exteditor_file    = NULL;
9230                         compose->exteditor_pid     = -1;
9231                         compose->exteditor_ch      = NULL;
9232                         compose->exteditor_tag     = -1;
9233                 } else
9234                         return FALSE;
9235         }
9236
9237         return TRUE;
9238 }
9239
9240 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9241                                  gpointer data)
9242 {
9243         gchar buf[3] = "3";
9244         Compose *compose = (Compose *)data;
9245         gsize bytes_read;
9246
9247         debug_print("Compose: input from monitoring process\n");
9248
9249         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9250
9251         g_io_channel_shutdown(source, FALSE, NULL);
9252         g_io_channel_unref(source);
9253
9254         waitpid(compose->exteditor_pid, NULL, 0);
9255
9256         if (buf[0] == '0') {            /* success */
9257                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9258                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9259
9260                 gtk_text_buffer_set_text(buffer, "", -1);
9261                 compose_insert_file(compose, compose->exteditor_file);
9262                 compose_changed_cb(NULL, compose);
9263                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9264
9265                 if (claws_unlink(compose->exteditor_file) < 0)
9266                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9267         } else if (buf[0] == '1') {     /* failed */
9268                 g_warning("Couldn't exec external editor\n");
9269                 if (claws_unlink(compose->exteditor_file) < 0)
9270                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9271         } else if (buf[0] == '2') {
9272                 g_warning("Couldn't write to file\n");
9273         } else if (buf[0] == '3') {
9274                 g_warning("Pipe read failed\n");
9275         }
9276
9277         compose_set_ext_editor_sensitive(compose, TRUE);
9278
9279         g_free(compose->exteditor_file);
9280         compose->exteditor_file    = NULL;
9281         compose->exteditor_pid     = -1;
9282         compose->exteditor_ch      = NULL;
9283         compose->exteditor_tag     = -1;
9284
9285         return FALSE;
9286 }
9287
9288 static void compose_set_ext_editor_sensitive(Compose *compose,
9289                                              gboolean sensitive)
9290 {
9291         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9292         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9293         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9294         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9295         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", sensitive);
9296         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9297         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9298         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9299
9300         gtk_widget_set_sensitive(compose->text,                       sensitive);
9301         if (compose->toolbar->send_btn)
9302                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9303         if (compose->toolbar->sendl_btn)
9304                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9305         if (compose->toolbar->draft_btn)
9306                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9307         if (compose->toolbar->insert_btn)
9308                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9309         if (compose->toolbar->sig_btn)
9310                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9311         if (compose->toolbar->exteditor_btn)
9312                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9313         if (compose->toolbar->linewrap_current_btn)
9314                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9315         if (compose->toolbar->linewrap_all_btn)
9316                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9317 }
9318 #endif /* G_OS_UNIX */
9319
9320 /**
9321  * compose_undo_state_changed:
9322  *
9323  * Change the sensivity of the menuentries undo and redo
9324  **/
9325 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9326                                        gint redo_state, gpointer data)
9327 {
9328         Compose *compose = (Compose *)data;
9329
9330         switch (undo_state) {
9331         case UNDO_STATE_TRUE:
9332                 if (!undostruct->undo_state) {
9333                         undostruct->undo_state = TRUE;
9334                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9335                 }
9336                 break;
9337         case UNDO_STATE_FALSE:
9338                 if (undostruct->undo_state) {
9339                         undostruct->undo_state = FALSE;
9340                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9341                 }
9342                 break;
9343         case UNDO_STATE_UNCHANGED:
9344                 break;
9345         case UNDO_STATE_REFRESH:
9346                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9347                 break;
9348         default:
9349                 g_warning("Undo state not recognized");
9350                 break;
9351         }
9352
9353         switch (redo_state) {
9354         case UNDO_STATE_TRUE:
9355                 if (!undostruct->redo_state) {
9356                         undostruct->redo_state = TRUE;
9357                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9358                 }
9359                 break;
9360         case UNDO_STATE_FALSE:
9361                 if (undostruct->redo_state) {
9362                         undostruct->redo_state = FALSE;
9363                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9364                 }
9365                 break;
9366         case UNDO_STATE_UNCHANGED:
9367                 break;
9368         case UNDO_STATE_REFRESH:
9369                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9370                 break;
9371         default:
9372                 g_warning("Redo state not recognized");
9373                 break;
9374         }
9375 }
9376
9377 /* callback functions */
9378
9379 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9380                                         GtkAllocation *allocation,
9381                                         Compose *compose)
9382 {
9383         prefs_common.compose_notebook_height = allocation->height;
9384 }
9385
9386 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9387  * includes "non-client" (windows-izm) in calculation, so this calculation
9388  * may not be accurate.
9389  */
9390 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9391                                         GtkAllocation *allocation,
9392                                         GtkSHRuler *shruler)
9393 {
9394         if (prefs_common.show_ruler) {
9395                 gint char_width = 0, char_height = 0;
9396                 gint line_width_in_chars;
9397
9398                 gtkut_get_font_size(GTK_WIDGET(widget),
9399                                     &char_width, &char_height);
9400                 line_width_in_chars =
9401                         (allocation->width - allocation->x) / char_width;
9402
9403                 /* got the maximum */
9404                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9405                                     0.0, line_width_in_chars, 0);
9406         }
9407
9408         return TRUE;
9409 }
9410
9411 typedef struct {
9412         gchar                   *header;
9413         gchar                   *entry;
9414         ComposePrefType         type;
9415         gboolean                entry_marked;
9416 } HeaderEntryState;
9417
9418 static void account_activated(GtkComboBox *optmenu, gpointer data)
9419 {
9420         Compose *compose = (Compose *)data;
9421
9422         PrefsAccount *ac;
9423         gchar *folderidentifier;
9424         gint account_id = 0;
9425         GtkTreeModel *menu;
9426         GtkTreeIter iter;
9427         GSList *list, *saved_list = NULL;
9428         HeaderEntryState *state;
9429         GtkRcStyle *style = NULL;
9430 #if !GTK_CHECK_VERSION(3, 0, 0)
9431         static GdkColor yellow;
9432         static gboolean color_set = FALSE;
9433 #else
9434         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9435 #endif
9436
9437         /* Get ID of active account in the combo box */
9438         menu = gtk_combo_box_get_model(optmenu);
9439         gtk_combo_box_get_active_iter(optmenu, &iter);
9440         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9441
9442         ac = account_find_from_id(account_id);
9443         cm_return_if_fail(ac != NULL);
9444
9445         if (ac != compose->account) {
9446                 compose_select_account(compose, ac, FALSE);
9447
9448                 for (list = compose->header_list; list; list = list->next) {
9449                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9450                         
9451                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9452                                 compose_destroy_headerentry(compose, hentry);
9453                                 continue;
9454                         }
9455                         
9456                         state = g_malloc0(sizeof(HeaderEntryState));
9457                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9458                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9459                         state->entry = gtk_editable_get_chars(
9460                                         GTK_EDITABLE(hentry->entry), 0, -1);
9461                         state->type = hentry->type;
9462                                 
9463 #if !GTK_CHECK_VERSION(3, 0, 0)
9464                         if (!color_set) {
9465                                 gdk_color_parse("#f5f6be", &yellow);
9466                                 color_set = gdk_colormap_alloc_color(
9467                                                         gdk_colormap_get_system(),
9468                                                         &yellow, FALSE, TRUE);
9469                         }
9470 #endif
9471                                 
9472                         style = gtk_widget_get_modifier_style(hentry->entry);
9473                         state->entry_marked = gdk_color_equal(&yellow,
9474                                                 &style->base[GTK_STATE_NORMAL]);
9475
9476                         saved_list = g_slist_append(saved_list, state);
9477                         compose_destroy_headerentry(compose, hentry);
9478                 }
9479
9480                 compose->header_last = NULL;
9481                 g_slist_free(compose->header_list);
9482                 compose->header_list = NULL;
9483                 compose->header_nextrow = 1;
9484                 compose_create_header_entry(compose);
9485                 
9486                 if (ac->set_autocc && ac->auto_cc)
9487                         compose_entry_append(compose, ac->auto_cc,
9488                                                 COMPOSE_CC, PREF_ACCOUNT);
9489
9490                 if (ac->set_autobcc && ac->auto_bcc) 
9491                         compose_entry_append(compose, ac->auto_bcc,
9492                                                 COMPOSE_BCC, PREF_ACCOUNT);
9493         
9494                 if (ac->set_autoreplyto && ac->auto_replyto)
9495                         compose_entry_append(compose, ac->auto_replyto,
9496                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9497                 
9498                 for (list = saved_list; list; list = list->next) {
9499                         state = (HeaderEntryState *) list->data;
9500                         
9501                         compose_add_header_entry(compose, state->header,
9502                                                 state->entry, state->type);
9503                         if (state->entry_marked)
9504                                 compose_entry_mark_default_to(compose, state->entry);
9505                         
9506                         g_free(state->header);  
9507                         g_free(state->entry);
9508                         g_free(state);
9509                 }
9510                 g_slist_free(saved_list);
9511                 
9512                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9513                                         (ac->protocol == A_NNTP) ? 
9514                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9515         }
9516
9517         /* Set message save folder */
9518         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9519                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9520         }
9521         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9522                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9523                            
9524         compose_set_save_to(compose, NULL);
9525         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9526                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9527                                   (compose->account, F_OUTBOX));
9528                 compose_set_save_to(compose, folderidentifier);
9529                 g_free(folderidentifier);
9530         }
9531 }
9532
9533 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9534                             GtkTreeViewColumn *column, Compose *compose)
9535 {
9536         compose_attach_property(NULL, compose);
9537 }
9538
9539 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9540                                       gpointer data)
9541 {
9542         Compose *compose = (Compose *)data;
9543         GtkTreeSelection *attach_selection;
9544         gint attach_nr_selected;
9545         
9546         if (!event) return FALSE;
9547
9548         if (event->button == 3) {
9549                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9550                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9551                         
9552                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9553                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9554                         
9555                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9556                                NULL, NULL, event->button, event->time);
9557                 return TRUE;                           
9558         }
9559
9560         return FALSE;
9561 }
9562
9563 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9564                                    gpointer data)
9565 {
9566         Compose *compose = (Compose *)data;
9567
9568         if (!event) return FALSE;
9569
9570         switch (event->keyval) {
9571         case GDK_KEY_Delete:
9572                 compose_attach_remove_selected(NULL, compose);
9573                 break;
9574         }
9575         return FALSE;
9576 }
9577
9578 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9579 {
9580         toolbar_comp_set_sensitive(compose, allow);
9581         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9582         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9583 #if USE_ENCHANT
9584         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9585 #endif  
9586         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9587         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9588         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9589         
9590         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9591
9592 }
9593
9594 static void compose_send_cb(GtkAction *action, gpointer data)
9595 {
9596         Compose *compose = (Compose *)data;
9597
9598         if (prefs_common.work_offline && 
9599             !inc_offline_should_override(TRUE,
9600                 _("Claws Mail needs network access in order "
9601                   "to send this email.")))
9602                 return;
9603         
9604         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9605                 g_source_remove(compose->draft_timeout_tag);
9606                 compose->draft_timeout_tag = -1;
9607         }
9608
9609         compose_send(compose);
9610 }
9611
9612 static void compose_send_later_cb(GtkAction *action, gpointer data)
9613 {
9614         Compose *compose = (Compose *)data;
9615         gint val;
9616
9617         inc_lock();
9618         compose_allow_user_actions(compose, FALSE);
9619         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9620         compose_allow_user_actions(compose, TRUE);
9621         inc_unlock();
9622
9623         if (!val) {
9624                 compose_close(compose);
9625         } else if (val == -1) {
9626                 alertpanel_error(_("Could not queue message."));
9627         } else if (val == -2) {
9628                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9629         } else if (val == -3) {
9630                 if (privacy_peek_error())
9631                 alertpanel_error(_("Could not queue message for sending:\n\n"
9632                                    "Signature failed: %s"), privacy_get_error());
9633         } else if (val == -4) {
9634                 alertpanel_error(_("Could not queue message for sending:\n\n"
9635                                    "Charset conversion failed."));
9636         } else if (val == -5) {
9637                 alertpanel_error(_("Could not queue message for sending:\n\n"
9638                                    "Couldn't get recipient encryption key."));
9639         } else if (val == -6) {
9640                 /* silent error */
9641         }
9642         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9643 }
9644
9645 #define DRAFTED_AT_EXIT "drafted_at_exit"
9646 static void compose_register_draft(MsgInfo *info)
9647 {
9648         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9649                                       DRAFTED_AT_EXIT, NULL);
9650         FILE *fp = g_fopen(filepath, "ab");
9651         
9652         if (fp) {
9653                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9654                                 info->msgnum);
9655                 fclose(fp);
9656         }
9657                 
9658         g_free(filepath);       
9659 }
9660
9661 gboolean compose_draft (gpointer data, guint action) 
9662 {
9663         Compose *compose = (Compose *)data;
9664         FolderItem *draft;
9665         gchar *tmp;
9666         gchar *sheaders;
9667         gint msgnum;
9668         MsgFlags flag = {0, 0};
9669         static gboolean lock = FALSE;
9670         MsgInfo *newmsginfo;
9671         FILE *fp;
9672         gboolean target_locked = FALSE;
9673         gboolean err = FALSE;
9674
9675         if (lock) return FALSE;
9676
9677         if (compose->sending)
9678                 return TRUE;
9679
9680         draft = account_get_special_folder(compose->account, F_DRAFT);
9681         cm_return_val_if_fail(draft != NULL, FALSE);
9682         
9683         if (!g_mutex_trylock(compose->mutex)) {
9684                 /* we don't want to lock the mutex once it's available,
9685                  * because as the only other part of compose.c locking
9686                  * it is compose_close - which means once unlocked,
9687                  * the compose struct will be freed */
9688                 debug_print("couldn't lock mutex, probably sending\n");
9689                 return FALSE;
9690         }
9691         
9692         lock = TRUE;
9693
9694         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9695                               G_DIR_SEPARATOR, compose);
9696         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9697                 FILE_OP_ERROR(tmp, "fopen");
9698                 goto warn_err;
9699         }
9700
9701         /* chmod for security */
9702         if (change_file_mode_rw(fp, tmp) < 0) {
9703                 FILE_OP_ERROR(tmp, "chmod");
9704                 g_warning("can't change file mode\n");
9705         }
9706
9707         /* Save draft infos */
9708         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9709         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9710
9711         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9712                 gchar *savefolderid;
9713
9714                 savefolderid = compose_get_save_to(compose);
9715                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9716                 g_free(savefolderid);
9717         }
9718         if (compose->return_receipt) {
9719                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9720         }
9721         if (compose->privacy_system) {
9722                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9723                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9724                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9725         }
9726
9727         /* Message-ID of message replying to */
9728         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9729                 gchar *folderid;
9730                 
9731                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9732                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9733                 g_free(folderid);
9734         }
9735         /* Message-ID of message forwarding to */
9736         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9737                 gchar *folderid;
9738                 
9739                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9740                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9741                 g_free(folderid);
9742         }
9743
9744         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9745         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9746
9747         sheaders = compose_get_manual_headers_info(compose);
9748         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9749         g_free(sheaders);
9750
9751         /* end of headers */
9752         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9753
9754         if (err) {
9755                 fclose(fp);
9756                 goto warn_err;
9757         }
9758
9759         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9760                 fclose(fp);
9761                 goto warn_err;
9762         }
9763         if (fclose(fp) == EOF) {
9764                 goto warn_err;
9765         }
9766         
9767         if (compose->targetinfo) {
9768                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9769                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9770         }
9771         flag.tmp_flags = MSG_DRAFT;
9772
9773         folder_item_scan(draft);
9774         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9775                 MsgInfo *tmpinfo = NULL;
9776                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9777                 if (compose->msgid) {
9778                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9779                 }
9780                 if (tmpinfo) {
9781                         msgnum = tmpinfo->msgnum;
9782                         procmsg_msginfo_free(tmpinfo);
9783                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9784                 } else {
9785                         debug_print("didn't get draft msgnum after scanning\n");
9786                 }
9787         } else {
9788                 debug_print("got draft msgnum %d from adding\n", msgnum);
9789         }
9790         if (msgnum < 0) {
9791 warn_err:
9792                 claws_unlink(tmp);
9793                 g_free(tmp);
9794                 if (action != COMPOSE_AUTO_SAVE) {
9795                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9796                                 alertpanel_error(_("Could not save draft."));
9797                         else {
9798                                 AlertValue val;
9799                                 gtkut_window_popup(compose->window);
9800                                 val = alertpanel_full(_("Could not save draft"),
9801                                         _("Could not save draft.\n"
9802                                         "Do you want to cancel exit or discard this email?"),
9803                                           _("_Cancel exit"), _("_Discard email"), NULL,
9804                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9805                                 if (val == G_ALERTALTERNATE) {
9806                                         lock = FALSE;
9807                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9808                                         compose_close(compose);
9809                                         return TRUE;
9810                                 } else {
9811                                         lock = FALSE;
9812                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9813                                         return FALSE;
9814                                 }
9815                         }
9816                 }
9817                 goto unlock;
9818         }
9819         g_free(tmp);
9820
9821         if (compose->mode == COMPOSE_REEDIT) {
9822                 compose_remove_reedit_target(compose, TRUE);
9823         }
9824
9825         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9826
9827         if (newmsginfo) {
9828                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9829                 if (target_locked)
9830                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9831                 else
9832                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9833                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9834                         procmsg_msginfo_set_flags(newmsginfo, 0,
9835                                                   MSG_HAS_ATTACHMENT);
9836
9837                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9838                         compose_register_draft(newmsginfo);
9839                 }
9840                 procmsg_msginfo_free(newmsginfo);
9841         }
9842         
9843         folder_item_scan(draft);
9844         
9845         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9846                 lock = FALSE;
9847                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9848                 compose_close(compose);
9849                 return TRUE;
9850         } else {
9851                 struct stat s;
9852                 gchar *path;
9853
9854                 path = folder_item_fetch_msg(draft, msgnum);
9855                 if (path == NULL) {
9856                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9857                         goto unlock;
9858                 }
9859                 if (g_stat(path, &s) < 0) {
9860                         FILE_OP_ERROR(path, "stat");
9861                         g_free(path);
9862                         goto unlock;
9863                 }
9864                 g_free(path);
9865
9866                 procmsg_msginfo_free(compose->targetinfo);
9867                 compose->targetinfo = procmsg_msginfo_new();
9868                 compose->targetinfo->msgnum = msgnum;
9869                 compose->targetinfo->size = (goffset)s.st_size;
9870                 compose->targetinfo->mtime = s.st_mtime;
9871                 compose->targetinfo->folder = draft;
9872                 if (target_locked)
9873                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9874                 compose->mode = COMPOSE_REEDIT;
9875                 
9876                 if (action == COMPOSE_AUTO_SAVE) {
9877                         compose->autosaved_draft = compose->targetinfo;
9878                 }
9879                 compose->modified = FALSE;
9880                 compose_set_title(compose);
9881         }
9882 unlock:
9883         lock = FALSE;
9884         g_mutex_unlock(compose->mutex);
9885         return TRUE;
9886 }
9887
9888 void compose_clear_exit_drafts(void)
9889 {
9890         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9891                                       DRAFTED_AT_EXIT, NULL);
9892         if (is_file_exist(filepath))
9893                 claws_unlink(filepath);
9894         
9895         g_free(filepath);
9896 }
9897
9898 void compose_reopen_exit_drafts(void)
9899 {
9900         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9901                                       DRAFTED_AT_EXIT, NULL);
9902         FILE *fp = g_fopen(filepath, "rb");
9903         gchar buf[1024];
9904         
9905         if (fp) {
9906                 while (fgets(buf, sizeof(buf), fp)) {
9907                         gchar **parts = g_strsplit(buf, "\t", 2);
9908                         const gchar *folder = parts[0];
9909                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9910                         
9911                         if (folder && *folder && msgnum > -1) {
9912                                 FolderItem *item = folder_find_item_from_identifier(folder);
9913                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9914                                 if (info)
9915                                         compose_reedit(info, FALSE);
9916                         }
9917                         g_strfreev(parts);
9918                 }       
9919                 fclose(fp);
9920         }       
9921         g_free(filepath);
9922         compose_clear_exit_drafts();
9923 }
9924
9925 static void compose_save_cb(GtkAction *action, gpointer data)
9926 {
9927         Compose *compose = (Compose *)data;
9928         compose_draft(compose, COMPOSE_KEEP_EDITING);
9929         compose->rmode = COMPOSE_REEDIT;
9930 }
9931
9932 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9933 {
9934         if (compose && file_list) {
9935                 GList *tmp;
9936
9937                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9938                         gchar *file = (gchar *) tmp->data;
9939                         gchar *utf8_filename = conv_filename_to_utf8(file);
9940                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9941                         compose_changed_cb(NULL, compose);
9942                         if (free_data) {
9943                         g_free(file);
9944                                 tmp->data = NULL;
9945                         }
9946                         g_free(utf8_filename);
9947                 }
9948         }
9949 }
9950
9951 static void compose_attach_cb(GtkAction *action, gpointer data)
9952 {
9953         Compose *compose = (Compose *)data;
9954         GList *file_list;
9955
9956         if (compose->redirect_filename != NULL)
9957                 return;
9958
9959         /* Set focus_window properly, in case we were called via popup menu,
9960          * which unsets it (via focus_out_event callback on compose window). */
9961         manage_window_focus_in(compose->window, NULL, NULL);
9962
9963         file_list = filesel_select_multiple_files_open(_("Select file"));
9964
9965         if (file_list) {
9966                 compose_attach_from_list(compose, file_list, TRUE);
9967                 g_list_free(file_list);
9968         }
9969 }
9970
9971 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9972 {
9973         Compose *compose = (Compose *)data;
9974         GList *file_list;
9975         gint files_inserted = 0;
9976
9977         file_list = filesel_select_multiple_files_open(_("Select file"));
9978
9979         if (file_list) {
9980                 GList *tmp;
9981
9982                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9983                         gchar *file = (gchar *) tmp->data;
9984                         gchar *filedup = g_strdup(file);
9985                         gchar *shortfile = g_path_get_basename(filedup);
9986                         ComposeInsertResult res;
9987                         /* insert the file if the file is short or if the user confirmed that
9988                            he/she wants to insert the large file */
9989                         res = compose_insert_file(compose, file);
9990                         if (res == COMPOSE_INSERT_READ_ERROR) {
9991                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9992                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9993                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9994                                                         "for the current encoding, insertion may be incorrect."),
9995                                                         shortfile);
9996                         } else if (res == COMPOSE_INSERT_SUCCESS)
9997                                 files_inserted++;
9998
9999                         g_free(shortfile);
10000                         g_free(filedup);
10001                         g_free(file);
10002                 }
10003                 g_list_free(file_list);
10004         }
10005
10006 #ifdef USE_ENCHANT      
10007         if (files_inserted > 0 && compose->gtkaspell && 
10008             compose->gtkaspell->check_while_typing)
10009                 gtkaspell_highlight_all(compose->gtkaspell);
10010 #endif
10011 }
10012
10013 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10014 {
10015         Compose *compose = (Compose *)data;
10016
10017         compose_insert_sig(compose, FALSE);
10018 }
10019
10020 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10021 {
10022         Compose *compose = (Compose *)data;
10023
10024         compose_insert_sig(compose, TRUE);
10025 }
10026
10027 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10028                               gpointer data)
10029 {
10030         gint x, y;
10031         Compose *compose = (Compose *)data;
10032
10033         gtkut_widget_get_uposition(widget, &x, &y);
10034         if (!compose->batch) {
10035                 prefs_common.compose_x = x;
10036                 prefs_common.compose_y = y;
10037         }
10038         if (compose->sending || compose->updating)
10039                 return TRUE;
10040         compose_close_cb(NULL, compose);
10041         return TRUE;
10042 }
10043
10044 void compose_close_toolbar(Compose *compose)
10045 {
10046         compose_close_cb(NULL, compose);
10047 }
10048
10049 static void compose_close_cb(GtkAction *action, gpointer data)
10050 {
10051         Compose *compose = (Compose *)data;
10052         AlertValue val;
10053
10054 #ifdef G_OS_UNIX
10055         if (compose->exteditor_tag != -1) {
10056                 if (!compose_ext_editor_kill(compose))
10057                         return;
10058         }
10059 #endif
10060
10061         if (compose->modified) {
10062                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10063                 if (!g_mutex_trylock(compose->mutex)) {
10064                         /* we don't want to lock the mutex once it's available,
10065                          * because as the only other part of compose.c locking
10066                          * it is compose_close - which means once unlocked,
10067                          * the compose struct will be freed */
10068                         debug_print("couldn't lock mutex, probably sending\n");
10069                         return;
10070                 }
10071                 if (!reedit) {
10072                         val = alertpanel(_("Discard message"),
10073                                  _("This message has been modified. Discard it?"),
10074                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10075                 } else {
10076                         val = alertpanel(_("Save changes"),
10077                                  _("This message has been modified. Save the latest changes?"),
10078                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10079                 }
10080                 g_mutex_unlock(compose->mutex);
10081                 switch (val) {
10082                 case G_ALERTDEFAULT:
10083                         if (prefs_common.autosave && !reedit)
10084                                 compose_remove_draft(compose);                  
10085                         break;
10086                 case G_ALERTALTERNATE:
10087                         compose_draft(data, COMPOSE_QUIT_EDITING);
10088                         return;
10089                 default:
10090                         return;
10091                 }
10092         }
10093
10094         compose_close(compose);
10095 }
10096
10097 static void compose_print_cb(GtkAction *action, gpointer data)
10098 {
10099         Compose *compose = (Compose *) data;
10100
10101         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10102         if (compose->targetinfo)
10103                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10104 }
10105
10106 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10107 {
10108         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10109         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10110         Compose *compose = (Compose *) data;
10111
10112         if (active)
10113                 compose->out_encoding = (CharSet)value;
10114 }
10115
10116 static void compose_address_cb(GtkAction *action, gpointer data)
10117 {
10118         Compose *compose = (Compose *)data;
10119
10120 #ifndef USE_NEW_ADDRBOOK
10121         addressbook_open(compose);
10122 #else
10123         GError* error = NULL;
10124         addressbook_connect_signals(compose);
10125         addressbook_dbus_open(TRUE, &error);
10126         if (error) {
10127                 g_warning("%s", error->message);
10128                 g_error_free(error);
10129         }
10130 #endif
10131 }
10132
10133 static void about_show_cb(GtkAction *action, gpointer data)
10134 {
10135         about_show();
10136 }
10137
10138 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10139 {
10140         Compose *compose = (Compose *)data;
10141         Template *tmpl;
10142         gchar *msg;
10143         AlertValue val;
10144
10145         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10146         cm_return_if_fail(tmpl != NULL);
10147
10148         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10149                               tmpl->name);
10150         val = alertpanel(_("Apply template"), msg,
10151                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10152         g_free(msg);
10153
10154         if (val == G_ALERTDEFAULT)
10155                 compose_template_apply(compose, tmpl, TRUE);
10156         else if (val == G_ALERTALTERNATE)
10157                 compose_template_apply(compose, tmpl, FALSE);
10158 }
10159
10160 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10161 {
10162         Compose *compose = (Compose *)data;
10163
10164         compose_exec_ext_editor(compose);
10165 }
10166
10167 static void compose_undo_cb(GtkAction *action, gpointer data)
10168 {
10169         Compose *compose = (Compose *)data;
10170         gboolean prev_autowrap = compose->autowrap;
10171
10172         compose->autowrap = FALSE;
10173         undo_undo(compose->undostruct);
10174         compose->autowrap = prev_autowrap;
10175 }
10176
10177 static void compose_redo_cb(GtkAction *action, gpointer data)
10178 {
10179         Compose *compose = (Compose *)data;
10180         gboolean prev_autowrap = compose->autowrap;
10181         
10182         compose->autowrap = FALSE;
10183         undo_redo(compose->undostruct);
10184         compose->autowrap = prev_autowrap;
10185 }
10186
10187 static void entry_cut_clipboard(GtkWidget *entry)
10188 {
10189         if (GTK_IS_EDITABLE(entry))
10190                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10191         else if (GTK_IS_TEXT_VIEW(entry))
10192                 gtk_text_buffer_cut_clipboard(
10193                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10194                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10195                         TRUE);
10196 }
10197
10198 static void entry_copy_clipboard(GtkWidget *entry)
10199 {
10200         if (GTK_IS_EDITABLE(entry))
10201                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10202         else if (GTK_IS_TEXT_VIEW(entry))
10203                 gtk_text_buffer_copy_clipboard(
10204                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10205                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10206 }
10207
10208 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10209                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10210 {
10211         if (GTK_IS_TEXT_VIEW(entry)) {
10212                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10213                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10214                 GtkTextIter start_iter, end_iter;
10215                 gint start, end;
10216                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10217
10218                 if (contents == NULL)
10219                         return;
10220         
10221                 /* we shouldn't delete the selection when middle-click-pasting, or we
10222                  * can't mid-click-paste our own selection */
10223                 if (clip != GDK_SELECTION_PRIMARY) {
10224                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10225                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10226                 }
10227                 
10228                 if (insert_place == NULL) {
10229                         /* if insert_place isn't specified, insert at the cursor.
10230                          * used for Ctrl-V pasting */
10231                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10232                         start = gtk_text_iter_get_offset(&start_iter);
10233                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10234                 } else {
10235                         /* if insert_place is specified, paste here.
10236                          * used for mid-click-pasting */
10237                         start = gtk_text_iter_get_offset(insert_place);
10238                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10239                         if (prefs_common.primary_paste_unselects)
10240                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10241                 }
10242                 
10243                 if (!wrap) {
10244                         /* paste unwrapped: mark the paste so it's not wrapped later */
10245                         end = start + strlen(contents);
10246                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10247                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10248                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10249                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10250                         /* rewrap paragraph now (after a mid-click-paste) */
10251                         mark_start = gtk_text_buffer_get_insert(buffer);
10252                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10253                         gtk_text_iter_backward_char(&start_iter);
10254                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10255                 }
10256         } else if (GTK_IS_EDITABLE(entry))
10257                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10258
10259         compose->modified = TRUE;
10260 }
10261
10262 static void entry_allsel(GtkWidget *entry)
10263 {
10264         if (GTK_IS_EDITABLE(entry))
10265                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10266         else if (GTK_IS_TEXT_VIEW(entry)) {
10267                 GtkTextIter startiter, enditer;
10268                 GtkTextBuffer *textbuf;
10269
10270                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10271                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10272                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10273
10274                 gtk_text_buffer_move_mark_by_name(textbuf, 
10275                         "selection_bound", &startiter);
10276                 gtk_text_buffer_move_mark_by_name(textbuf, 
10277                         "insert", &enditer);
10278         }
10279 }
10280
10281 static void compose_cut_cb(GtkAction *action, gpointer data)
10282 {
10283         Compose *compose = (Compose *)data;
10284         if (compose->focused_editable 
10285 #ifndef GENERIC_UMPC
10286             && gtk_widget_has_focus(compose->focused_editable)
10287 #endif
10288             )
10289                 entry_cut_clipboard(compose->focused_editable);
10290 }
10291
10292 static void compose_copy_cb(GtkAction *action, gpointer data)
10293 {
10294         Compose *compose = (Compose *)data;
10295         if (compose->focused_editable 
10296 #ifndef GENERIC_UMPC
10297             && gtk_widget_has_focus(compose->focused_editable)
10298 #endif
10299             )
10300                 entry_copy_clipboard(compose->focused_editable);
10301 }
10302
10303 static void compose_paste_cb(GtkAction *action, gpointer data)
10304 {
10305         Compose *compose = (Compose *)data;
10306         gint prev_autowrap;
10307         GtkTextBuffer *buffer;
10308         BLOCK_WRAP();
10309         if (compose->focused_editable &&
10310 #ifndef GENERIC_UMPC
10311             gtk_widget_has_focus(compose->focused_editable)
10312 #endif
10313                 )
10314                 entry_paste_clipboard(compose, compose->focused_editable, 
10315                                 prefs_common.linewrap_pastes,
10316                                 GDK_SELECTION_CLIPBOARD, NULL);
10317         UNBLOCK_WRAP();
10318
10319 #ifdef USE_ENCHANT
10320         if (
10321 #ifndef GENERIC_UMPC
10322                 gtk_widget_has_focus(compose->text) &&
10323 #endif
10324             compose->gtkaspell && 
10325             compose->gtkaspell->check_while_typing)
10326                 gtkaspell_highlight_all(compose->gtkaspell);
10327 #endif
10328 }
10329
10330 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10331 {
10332         Compose *compose = (Compose *)data;
10333         gint wrap_quote = prefs_common.linewrap_quote;
10334         if (compose->focused_editable 
10335 #ifndef GENERIC_UMPC
10336             && gtk_widget_has_focus(compose->focused_editable)
10337 #endif
10338             ) {
10339                 /* let text_insert() (called directly or at a later time
10340                  * after the gtk_editable_paste_clipboard) know that 
10341                  * text is to be inserted as a quotation. implemented
10342                  * by using a simple refcount... */
10343                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10344                                                 G_OBJECT(compose->focused_editable),
10345                                                 "paste_as_quotation"));
10346                 g_object_set_data(G_OBJECT(compose->focused_editable),
10347                                     "paste_as_quotation",
10348                                     GINT_TO_POINTER(paste_as_quotation + 1));
10349                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10350                 entry_paste_clipboard(compose, compose->focused_editable, 
10351                                 prefs_common.linewrap_pastes,
10352                                 GDK_SELECTION_CLIPBOARD, NULL);
10353                 prefs_common.linewrap_quote = wrap_quote;
10354         }
10355 }
10356
10357 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10358 {
10359         Compose *compose = (Compose *)data;
10360         gint prev_autowrap;
10361         GtkTextBuffer *buffer;
10362         BLOCK_WRAP();
10363         if (compose->focused_editable 
10364 #ifndef GENERIC_UMPC
10365             && gtk_widget_has_focus(compose->focused_editable)
10366 #endif
10367             )
10368                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10369                         GDK_SELECTION_CLIPBOARD, NULL);
10370         UNBLOCK_WRAP();
10371
10372 #ifdef USE_ENCHANT
10373         if (
10374 #ifndef GENERIC_UMPC
10375                 gtk_widget_has_focus(compose->text) &&
10376 #endif
10377             compose->gtkaspell && 
10378             compose->gtkaspell->check_while_typing)
10379                 gtkaspell_highlight_all(compose->gtkaspell);
10380 #endif
10381 }
10382
10383 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10384 {
10385         Compose *compose = (Compose *)data;
10386         gint prev_autowrap;
10387         GtkTextBuffer *buffer;
10388         BLOCK_WRAP();
10389         if (compose->focused_editable 
10390 #ifndef GENERIC_UMPC
10391             && gtk_widget_has_focus(compose->focused_editable)
10392 #endif
10393             )
10394                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10395                         GDK_SELECTION_CLIPBOARD, NULL);
10396         UNBLOCK_WRAP();
10397
10398 #ifdef USE_ENCHANT
10399         if (
10400 #ifndef GENERIC_UMPC
10401                 gtk_widget_has_focus(compose->text) &&
10402 #endif
10403             compose->gtkaspell &&
10404             compose->gtkaspell->check_while_typing)
10405                 gtkaspell_highlight_all(compose->gtkaspell);
10406 #endif
10407 }
10408
10409 static void compose_allsel_cb(GtkAction *action, gpointer data)
10410 {
10411         Compose *compose = (Compose *)data;
10412         if (compose->focused_editable 
10413 #ifndef GENERIC_UMPC
10414             && gtk_widget_has_focus(compose->focused_editable)
10415 #endif
10416             )
10417                 entry_allsel(compose->focused_editable);
10418 }
10419
10420 static void textview_move_beginning_of_line (GtkTextView *text)
10421 {
10422         GtkTextBuffer *buffer;
10423         GtkTextMark *mark;
10424         GtkTextIter ins;
10425
10426         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10427
10428         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10429         mark = gtk_text_buffer_get_insert(buffer);
10430         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10431         gtk_text_iter_set_line_offset(&ins, 0);
10432         gtk_text_buffer_place_cursor(buffer, &ins);
10433 }
10434
10435 static void textview_move_forward_character (GtkTextView *text)
10436 {
10437         GtkTextBuffer *buffer;
10438         GtkTextMark *mark;
10439         GtkTextIter ins;
10440
10441         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10442
10443         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10444         mark = gtk_text_buffer_get_insert(buffer);
10445         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10446         if (gtk_text_iter_forward_cursor_position(&ins))
10447                 gtk_text_buffer_place_cursor(buffer, &ins);
10448 }
10449
10450 static void textview_move_backward_character (GtkTextView *text)
10451 {
10452         GtkTextBuffer *buffer;
10453         GtkTextMark *mark;
10454         GtkTextIter ins;
10455
10456         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10457
10458         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10459         mark = gtk_text_buffer_get_insert(buffer);
10460         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10461         if (gtk_text_iter_backward_cursor_position(&ins))
10462                 gtk_text_buffer_place_cursor(buffer, &ins);
10463 }
10464
10465 static void textview_move_forward_word (GtkTextView *text)
10466 {
10467         GtkTextBuffer *buffer;
10468         GtkTextMark *mark;
10469         GtkTextIter ins;
10470         gint count;
10471
10472         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10473
10474         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10475         mark = gtk_text_buffer_get_insert(buffer);
10476         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10477         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10478         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10479                 gtk_text_iter_backward_word_start(&ins);
10480                 gtk_text_buffer_place_cursor(buffer, &ins);
10481         }
10482 }
10483
10484 static void textview_move_backward_word (GtkTextView *text)
10485 {
10486         GtkTextBuffer *buffer;
10487         GtkTextMark *mark;
10488         GtkTextIter ins;
10489
10490         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10491
10492         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10493         mark = gtk_text_buffer_get_insert(buffer);
10494         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10495         if (gtk_text_iter_backward_word_starts(&ins, 1))
10496                 gtk_text_buffer_place_cursor(buffer, &ins);
10497 }
10498
10499 static void textview_move_end_of_line (GtkTextView *text)
10500 {
10501         GtkTextBuffer *buffer;
10502         GtkTextMark *mark;
10503         GtkTextIter ins;
10504
10505         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10506
10507         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10508         mark = gtk_text_buffer_get_insert(buffer);
10509         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10510         if (gtk_text_iter_forward_to_line_end(&ins))
10511                 gtk_text_buffer_place_cursor(buffer, &ins);
10512 }
10513
10514 static void textview_move_next_line (GtkTextView *text)
10515 {
10516         GtkTextBuffer *buffer;
10517         GtkTextMark *mark;
10518         GtkTextIter ins;
10519         gint offset;
10520
10521         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10522
10523         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10524         mark = gtk_text_buffer_get_insert(buffer);
10525         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10526         offset = gtk_text_iter_get_line_offset(&ins);
10527         if (gtk_text_iter_forward_line(&ins)) {
10528                 gtk_text_iter_set_line_offset(&ins, offset);
10529                 gtk_text_buffer_place_cursor(buffer, &ins);
10530         }
10531 }
10532
10533 static void textview_move_previous_line (GtkTextView *text)
10534 {
10535         GtkTextBuffer *buffer;
10536         GtkTextMark *mark;
10537         GtkTextIter ins;
10538         gint offset;
10539
10540         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10541
10542         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10543         mark = gtk_text_buffer_get_insert(buffer);
10544         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10545         offset = gtk_text_iter_get_line_offset(&ins);
10546         if (gtk_text_iter_backward_line(&ins)) {
10547                 gtk_text_iter_set_line_offset(&ins, offset);
10548                 gtk_text_buffer_place_cursor(buffer, &ins);
10549         }
10550 }
10551
10552 static void textview_delete_forward_character (GtkTextView *text)
10553 {
10554         GtkTextBuffer *buffer;
10555         GtkTextMark *mark;
10556         GtkTextIter ins, end_iter;
10557
10558         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10559
10560         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10561         mark = gtk_text_buffer_get_insert(buffer);
10562         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10563         end_iter = ins;
10564         if (gtk_text_iter_forward_char(&end_iter)) {
10565                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10566         }
10567 }
10568
10569 static void textview_delete_backward_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_backward_char(&end_iter)) {
10582                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10583         }
10584 }
10585
10586 static void textview_delete_forward_word (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_forward_word_end(&end_iter)) {
10599                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10600         }
10601 }
10602
10603 static void textview_delete_backward_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_backward_word_start(&end_iter)) {
10616                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10617         }
10618 }
10619
10620 static void textview_delete_line (GtkTextView *text)
10621 {
10622         GtkTextBuffer *buffer;
10623         GtkTextMark *mark;
10624         GtkTextIter ins, start_iter, 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
10632         start_iter = ins;
10633         gtk_text_iter_set_line_offset(&start_iter, 0);
10634
10635         end_iter = ins;
10636         if (gtk_text_iter_ends_line(&end_iter)){
10637                 if (!gtk_text_iter_forward_char(&end_iter))
10638                         gtk_text_iter_backward_char(&start_iter);
10639         }
10640         else 
10641                 gtk_text_iter_forward_to_line_end(&end_iter);
10642         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10643 }
10644
10645 static void textview_delete_to_line_end (GtkTextView *text)
10646 {
10647         GtkTextBuffer *buffer;
10648         GtkTextMark *mark;
10649         GtkTextIter ins, end_iter;
10650
10651         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10652
10653         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10654         mark = gtk_text_buffer_get_insert(buffer);
10655         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10656         end_iter = ins;
10657         if (gtk_text_iter_ends_line(&end_iter))
10658                 gtk_text_iter_forward_char(&end_iter);
10659         else
10660                 gtk_text_iter_forward_to_line_end(&end_iter);
10661         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10662 }
10663
10664 #define DO_ACTION(name, act) {                                          \
10665         if(!strcmp(name, a_name)) {                                     \
10666                 return act;                                             \
10667         }                                                               \
10668 }
10669 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10670 {
10671         const gchar *a_name = gtk_action_get_name(action);
10672         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10673         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10674         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10675         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10676         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10677         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10678         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10679         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10680         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10681         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10682         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10683         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10684         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10685         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10686         return -1;
10687 }
10688
10689 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10690 {
10691         Compose *compose = (Compose *)data;
10692         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10693         ComposeCallAdvancedAction action = -1;
10694         
10695         action = compose_call_advanced_action_from_path(gaction);
10696
10697         static struct {
10698                 void (*do_action) (GtkTextView *text);
10699         } action_table[] = {
10700                 {textview_move_beginning_of_line},
10701                 {textview_move_forward_character},
10702                 {textview_move_backward_character},
10703                 {textview_move_forward_word},
10704                 {textview_move_backward_word},
10705                 {textview_move_end_of_line},
10706                 {textview_move_next_line},
10707                 {textview_move_previous_line},
10708                 {textview_delete_forward_character},
10709                 {textview_delete_backward_character},
10710                 {textview_delete_forward_word},
10711                 {textview_delete_backward_word},
10712                 {textview_delete_line},
10713                 {textview_delete_to_line_end}
10714         };
10715
10716         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10717
10718         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10719             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10720                 if (action_table[action].do_action)
10721                         action_table[action].do_action(text);
10722                 else
10723                         g_warning("Not implemented yet.");
10724         }
10725 }
10726
10727 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10728 {
10729         GtkAllocation allocation;
10730         GtkWidget *parent;
10731         gchar *str = NULL;
10732         
10733         if (GTK_IS_EDITABLE(widget)) {
10734                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10735                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10736                         strlen(str));
10737                 g_free(str);
10738                 if ((parent = gtk_widget_get_parent(widget))
10739                  && (parent = gtk_widget_get_parent(parent))
10740                  && (parent = gtk_widget_get_parent(parent))) {
10741                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
10742                                 gtk_widget_get_allocation(widget, &allocation);
10743                                 gint y = allocation.y;
10744                                 gint height = allocation.height;
10745                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10746                                         (GTK_SCROLLED_WINDOW(parent));
10747
10748                                 gfloat value = gtk_adjustment_get_value(shown);
10749                                 gfloat upper = gtk_adjustment_get_upper(shown);
10750                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
10751                                 if (y < (int)value) {
10752                                         gtk_adjustment_set_value(shown, y - 1);
10753                                 }
10754                                 if ((y + height) > ((int)value + (int)page_size)) {
10755                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
10756                                                 gtk_adjustment_set_value(shown, 
10757                                                         y + height - (int)page_size - 1);
10758                                         } else {
10759                                                 gtk_adjustment_set_value(shown, 
10760                                                         (int)upper - (int)page_size - 1);
10761                                         }
10762                                 }
10763                         }
10764                 }
10765         }
10766
10767         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10768                 compose->focused_editable = widget;
10769         
10770 #ifdef GENERIC_UMPC
10771         if (GTK_IS_TEXT_VIEW(widget) 
10772             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10773                 g_object_ref(compose->notebook);
10774                 g_object_ref(compose->edit_vbox);
10775                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10776                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10777                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10778                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10779                 g_object_unref(compose->notebook);
10780                 g_object_unref(compose->edit_vbox);
10781                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10782                                         G_CALLBACK(compose_grab_focus_cb),
10783                                         compose);
10784                 gtk_widget_grab_focus(widget);
10785                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10786                                         G_CALLBACK(compose_grab_focus_cb),
10787                                         compose);
10788         } else if (!GTK_IS_TEXT_VIEW(widget) 
10789                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
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->notebook);
10795                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
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         }
10806 #endif
10807 }
10808
10809 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10810 {
10811         compose->modified = TRUE;
10812 //      compose_beautify_paragraph(compose, NULL, TRUE);
10813 #ifndef GENERIC_UMPC
10814         compose_set_title(compose);
10815 #endif
10816 }
10817
10818 static void compose_wrap_cb(GtkAction *action, gpointer data)
10819 {
10820         Compose *compose = (Compose *)data;
10821         compose_beautify_paragraph(compose, NULL, TRUE);
10822 }
10823
10824 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10825 {
10826         Compose *compose = (Compose *)data;
10827         compose_wrap_all_full(compose, TRUE);
10828 }
10829
10830 static void compose_find_cb(GtkAction *action, gpointer data)
10831 {
10832         Compose *compose = (Compose *)data;
10833
10834         message_search_compose(compose);
10835 }
10836
10837 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10838                                          gpointer        data)
10839 {
10840         Compose *compose = (Compose *)data;
10841         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10842         if (compose->autowrap)
10843                 compose_wrap_all_full(compose, TRUE);
10844         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10845 }
10846
10847 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10848                                          gpointer        data)
10849 {
10850         Compose *compose = (Compose *)data;
10851         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10852 }
10853
10854 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10855 {
10856         Compose *compose = (Compose *)data;
10857
10858         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10859 }
10860
10861 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10862 {
10863         Compose *compose = (Compose *)data;
10864
10865         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10866 }
10867
10868 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10869 {
10870         g_free(compose->privacy_system);
10871
10872         compose->privacy_system = g_strdup(account->default_privacy_system);
10873         compose_update_privacy_system_menu_item(compose, warn);
10874 }
10875
10876 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10877 {
10878         Compose *compose = (Compose *)data;
10879
10880         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10881                 gtk_widget_show(compose->ruler_hbox);
10882                 prefs_common.show_ruler = TRUE;
10883         } else {
10884                 gtk_widget_hide(compose->ruler_hbox);
10885                 gtk_widget_queue_resize(compose->edit_vbox);
10886                 prefs_common.show_ruler = FALSE;
10887         }
10888 }
10889
10890 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10891                                              GdkDragContext     *context,
10892                                              gint                x,
10893                                              gint                y,
10894                                              GtkSelectionData   *data,
10895                                              guint               info,
10896                                              guint               time,
10897                                              gpointer            user_data)
10898 {
10899         Compose *compose = (Compose *)user_data;
10900         GList *list, *tmp;
10901         GdkAtom type;
10902
10903         type = gtk_selection_data_get_data_type(data);
10904         if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10905 #ifdef G_OS_WIN32
10906          || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10907 #endif
10908            ) && gtk_drag_get_source_widget(context) != 
10909                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10910                 list = uri_list_extract_filenames(
10911                         (const gchar *)gtk_selection_data_get_data(data));
10912                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10913                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10914                         compose_attach_append
10915                                 (compose, (const gchar *)tmp->data,
10916                                  utf8_filename, NULL, NULL);
10917                         g_free(utf8_filename);
10918                 }
10919                 if (list) compose_changed_cb(NULL, compose);
10920                 list_free_strings(list);
10921                 g_list_free(list);
10922         } else if (gtk_drag_get_source_widget(context) 
10923                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10924                 /* comes from our summaryview */
10925                 SummaryView * summaryview = NULL;
10926                 GSList * list = NULL, *cur = NULL;
10927                 
10928                 if (mainwindow_get_mainwindow())
10929                         summaryview = mainwindow_get_mainwindow()->summaryview;
10930                 
10931                 if (summaryview)
10932                         list = summary_get_selected_msg_list(summaryview);
10933                 
10934                 for (cur = list; cur; cur = cur->next) {
10935                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10936                         gchar *file = NULL;
10937                         if (msginfo)
10938                                 file = procmsg_get_message_file_full(msginfo, 
10939                                         TRUE, TRUE);
10940                         if (file) {
10941                                 compose_attach_append(compose, (const gchar *)file, 
10942                                         (const gchar *)file, "message/rfc822", NULL);
10943                                 g_free(file);
10944                         }
10945                 }
10946                 g_slist_free(list);
10947         }
10948 }
10949
10950 static gboolean compose_drag_drop(GtkWidget *widget,
10951                                   GdkDragContext *drag_context,
10952                                   gint x, gint y,
10953                                   guint time, gpointer user_data)
10954 {
10955         /* not handling this signal makes compose_insert_drag_received_cb
10956          * called twice */
10957         return TRUE;                                     
10958 }
10959
10960 static gboolean completion_set_focus_to_subject
10961                                         (GtkWidget    *widget,
10962                                          GdkEventKey  *event,
10963                                          Compose      *compose)
10964 {
10965         cm_return_val_if_fail(compose != NULL, FALSE);
10966
10967         /* make backtab move to subject field */
10968         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10969                 gtk_widget_grab_focus(compose->subject_entry);
10970                 return TRUE;
10971         }
10972         return FALSE;
10973 }
10974
10975 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10976                                              GdkDragContext     *drag_context,
10977                                              gint                x,
10978                                              gint                y,
10979                                              GtkSelectionData   *data,
10980                                              guint               info,
10981                                              guint               time,
10982                                              gpointer            user_data)
10983 {
10984         Compose *compose = (Compose *)user_data;
10985         GList *list, *tmp;
10986         GdkAtom type;
10987
10988         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10989          * does not work */
10990         type = gtk_selection_data_get_data_type(data);
10991 #ifndef G_OS_WIN32
10992         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10993 #else
10994         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10995 #endif
10996                 AlertValue val = G_ALERTDEFAULT;
10997                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10998
10999                 list = uri_list_extract_filenames(ddata);
11000                 if (list == NULL && strstr(ddata, "://")) {
11001                         /* Assume a list of no files, and data has ://, is a remote link */
11002                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11003                         gchar *tmpfile = get_tmp_file();
11004                         str_write_to_file(tmpdata, tmpfile);
11005                         g_free(tmpdata);  
11006                         compose_insert_file(compose, tmpfile);
11007                         claws_unlink(tmpfile);
11008                         g_free(tmpfile);
11009                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11010                         compose_beautify_paragraph(compose, NULL, TRUE);
11011                         return;
11012                 }
11013                 switch (prefs_common.compose_dnd_mode) {
11014                         case COMPOSE_DND_ASK:
11015                                 val = alertpanel_full(_("Insert or attach?"),
11016                                          _("Do you want to insert the contents of the file(s) "
11017                                            "into the message body, or attach it to the email?"),
11018                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
11019                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11020                                 break;
11021                         case COMPOSE_DND_INSERT:
11022                                 val = G_ALERTALTERNATE;
11023                                 break;
11024                         case COMPOSE_DND_ATTACH:
11025                                 val = G_ALERTOTHER;
11026                                 break;
11027                         default:
11028                                 /* unexpected case */
11029                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11030                 }
11031
11032                 if (val & G_ALERTDISABLE) {
11033                         val &= ~G_ALERTDISABLE;
11034                         /* remember what action to perform by default, only if we don't click Cancel */
11035                         if (val == G_ALERTALTERNATE)
11036                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11037                         else if (val == G_ALERTOTHER)
11038                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11039                 }
11040
11041                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11042                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11043                         list_free_strings(list);
11044                         g_list_free(list);
11045                         return;
11046                 } else if (val == G_ALERTOTHER) {
11047                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11048                         list_free_strings(list);
11049                         g_list_free(list);
11050                         return;
11051                 } 
11052
11053                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11054                         compose_insert_file(compose, (const gchar *)tmp->data);
11055                 }
11056                 list_free_strings(list);
11057                 g_list_free(list);
11058                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11059                 return;
11060         } else {
11061                 return;
11062         }
11063         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11064 }
11065
11066 static void compose_header_drag_received_cb (GtkWidget          *widget,
11067                                              GdkDragContext     *drag_context,
11068                                              gint                x,
11069                                              gint                y,
11070                                              GtkSelectionData   *data,
11071                                              guint               info,
11072                                              guint               time,
11073                                              gpointer            user_data)
11074 {
11075         GtkEditable *entry = (GtkEditable *)user_data;
11076         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11077
11078         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11079          * does not work */
11080
11081         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11082                 gchar *decoded=g_new(gchar, strlen(email));
11083                 int start = 0;
11084
11085                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11086                 gtk_editable_delete_text(entry, 0, -1);
11087                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11088                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11089                 g_free(decoded);
11090                 return;
11091         }
11092         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11093 }
11094
11095 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11096 {
11097         Compose *compose = (Compose *)data;
11098
11099         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11100                 compose->return_receipt = TRUE;
11101         else
11102                 compose->return_receipt = FALSE;
11103 }
11104
11105 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11106 {
11107         Compose *compose = (Compose *)data;
11108
11109         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11110                 compose->remove_references = TRUE;
11111         else
11112                 compose->remove_references = FALSE;
11113 }
11114
11115 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11116                                         ComposeHeaderEntry *headerentry)
11117 {
11118         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11119         return FALSE;
11120 }
11121
11122 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11123                                             GdkEventKey *event,
11124                                             ComposeHeaderEntry *headerentry)
11125 {
11126         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11127             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11128             !(event->state & GDK_MODIFIER_MASK) &&
11129             (event->keyval == GDK_KEY_BackSpace) &&
11130             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11131                 gtk_container_remove
11132                         (GTK_CONTAINER(headerentry->compose->header_table),
11133                          headerentry->combo);
11134                 gtk_container_remove
11135                         (GTK_CONTAINER(headerentry->compose->header_table),
11136                          headerentry->entry);
11137                 headerentry->compose->header_list =
11138                         g_slist_remove(headerentry->compose->header_list,
11139                                        headerentry);
11140                 g_free(headerentry);
11141         } else  if (event->keyval == GDK_KEY_Tab) {
11142                 if (headerentry->compose->header_last == headerentry) {
11143                         /* Override default next focus, and give it to subject_entry
11144                          * instead of notebook tabs
11145                          */
11146                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11147                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11148                         return TRUE;
11149                 }
11150         }
11151         return FALSE;
11152 }
11153
11154 static gboolean scroll_postpone(gpointer data)
11155 {
11156         Compose *compose = (Compose *)data;
11157
11158         cm_return_val_if_fail(!compose->batch, FALSE);
11159
11160         GTK_EVENTS_FLUSH();
11161         compose_show_first_last_header(compose, FALSE);
11162         return FALSE;
11163 }
11164
11165 static void compose_headerentry_changed_cb(GtkWidget *entry,
11166                                     ComposeHeaderEntry *headerentry)
11167 {
11168         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11169                 compose_create_header_entry(headerentry->compose);
11170                 g_signal_handlers_disconnect_matched
11171                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11172                          0, 0, NULL, NULL, headerentry);
11173
11174                 if (!headerentry->compose->batch)
11175                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11176         }
11177 }
11178
11179 static gboolean compose_defer_auto_save_draft(Compose *compose)
11180 {
11181         compose->draft_timeout_tag = -1;
11182         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11183         return FALSE;
11184 }
11185
11186 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11187 {
11188         GtkAdjustment *vadj;
11189
11190         cm_return_if_fail(compose);
11191         cm_return_if_fail(!compose->batch);
11192         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11193         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11194         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11195                                 gtk_widget_get_parent(compose->header_table)));
11196         gtk_adjustment_set_value(vadj, (show_first ?
11197                                 gtk_adjustment_get_lower(vadj) :
11198                                 (gtk_adjustment_get_upper(vadj) -
11199                                 gtk_adjustment_get_page_size(vadj))));
11200         gtk_adjustment_changed(vadj);
11201 }
11202
11203 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11204                           const gchar *text, gint len, Compose *compose)
11205 {
11206         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11207                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11208         GtkTextMark *mark;
11209
11210         cm_return_if_fail(text != NULL);
11211
11212         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11213                                         G_CALLBACK(text_inserted),
11214                                         compose);
11215         if (paste_as_quotation) {
11216                 gchar *new_text;
11217                 const gchar *qmark;
11218                 guint pos = 0;
11219                 GtkTextIter start_iter;
11220
11221                 if (len < 0)
11222                         len = strlen(text);
11223
11224                 new_text = g_strndup(text, len);
11225
11226                 qmark = compose_quote_char_from_context(compose);
11227
11228                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11229                 gtk_text_buffer_place_cursor(buffer, iter);
11230
11231                 pos = gtk_text_iter_get_offset(iter);
11232
11233                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11234                                                   _("Quote format error at line %d."));
11235                 quote_fmt_reset_vartable();
11236                 g_free(new_text);
11237                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11238                                   GINT_TO_POINTER(paste_as_quotation - 1));
11239                                   
11240                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11241                 gtk_text_buffer_place_cursor(buffer, iter);
11242                 gtk_text_buffer_delete_mark(buffer, mark);
11243
11244                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11245                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11246                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11247                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11248                 gtk_text_buffer_delete_mark(buffer, mark);
11249         } else {
11250                 if (strcmp(text, "\n") || compose->automatic_break
11251                 || gtk_text_iter_starts_line(iter)) {
11252                         GtkTextIter before_ins;
11253                         gtk_text_buffer_insert(buffer, iter, text, len);
11254                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11255                                 before_ins = *iter; 
11256                                 gtk_text_iter_backward_chars(&before_ins, len);
11257                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11258                         }
11259                 } else {
11260                         /* check if the preceding is just whitespace or quote */
11261                         GtkTextIter start_line;
11262                         gchar *tmp = NULL, *quote = NULL;
11263                         gint quote_len = 0, is_normal = 0;
11264                         start_line = *iter;
11265                         gtk_text_iter_set_line_offset(&start_line, 0); 
11266                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11267                         g_strstrip(tmp);
11268
11269                         if (*tmp == '\0') {
11270                                 is_normal = 1;
11271                         } else {
11272                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11273                                 if (quote)
11274                                         is_normal = 1;
11275                                 g_free(quote);
11276                         }
11277                         g_free(tmp);
11278                         
11279                         if (is_normal) {
11280                                 gtk_text_buffer_insert(buffer, iter, text, len);
11281                         } else {
11282                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11283                                         iter, text, len, "no_join", NULL);
11284                         }
11285                 }
11286         }
11287         
11288         if (!paste_as_quotation) {
11289                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11290                 compose_beautify_paragraph(compose, iter, FALSE);
11291                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11292                 gtk_text_buffer_delete_mark(buffer, mark);
11293         }
11294
11295         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11296                                           G_CALLBACK(text_inserted),
11297                                           compose);
11298         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11299
11300         if (prefs_common.autosave && 
11301             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11302             compose->draft_timeout_tag != -2 /* disabled while loading */)
11303                 compose->draft_timeout_tag = g_timeout_add
11304                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11305 }
11306
11307 #if USE_ENCHANT
11308 static void compose_check_all(GtkAction *action, gpointer data)
11309 {
11310         Compose *compose = (Compose *)data;
11311         if (!compose->gtkaspell)
11312                 return;
11313                 
11314         if (gtk_widget_has_focus(compose->subject_entry))
11315                 claws_spell_entry_check_all(
11316                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11317         else
11318                 gtkaspell_check_all(compose->gtkaspell);
11319 }
11320
11321 static void compose_highlight_all(GtkAction *action, gpointer data)
11322 {
11323         Compose *compose = (Compose *)data;
11324         if (compose->gtkaspell) {
11325                 claws_spell_entry_recheck_all(
11326                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11327                 gtkaspell_highlight_all(compose->gtkaspell);
11328         }
11329 }
11330
11331 static void compose_check_backwards(GtkAction *action, gpointer data)
11332 {
11333         Compose *compose = (Compose *)data;
11334         if (!compose->gtkaspell) {
11335                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11336                 return;
11337         }
11338
11339         if (gtk_widget_has_focus(compose->subject_entry))
11340                 claws_spell_entry_check_backwards(
11341                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11342         else
11343                 gtkaspell_check_backwards(compose->gtkaspell);
11344 }
11345
11346 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11347 {
11348         Compose *compose = (Compose *)data;
11349         if (!compose->gtkaspell) {
11350                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11351                 return;
11352         }
11353
11354         if (gtk_widget_has_focus(compose->subject_entry))
11355                 claws_spell_entry_check_forwards_go(
11356                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11357         else
11358                 gtkaspell_check_forwards_go(compose->gtkaspell);
11359 }
11360 #endif
11361
11362 /*!
11363  *\brief        Guess originating forward account from MsgInfo and several 
11364  *              "common preference" settings. Return NULL if no guess. 
11365  */
11366 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11367 {
11368         PrefsAccount *account = NULL;
11369         
11370         cm_return_val_if_fail(msginfo, NULL);
11371         cm_return_val_if_fail(msginfo->folder, NULL);
11372         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11373
11374         if (msginfo->folder->prefs->enable_default_account)
11375                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11376                 
11377         if (!account) 
11378                 account = msginfo->folder->folder->account;
11379                 
11380         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11381                 gchar *to;
11382                 Xstrdup_a(to, msginfo->to, return NULL);
11383                 extract_address(to);
11384                 account = account_find_from_address(to, FALSE);
11385         }
11386
11387         if (!account && prefs_common.forward_account_autosel) {
11388                 gchar cc[BUFFSIZE];
11389                 if (!procheader_get_header_from_msginfo
11390                         (msginfo, cc,sizeof cc , "Cc:")) { 
11391                         gchar *buf = cc + strlen("Cc:");
11392                         extract_address(buf);
11393                         account = account_find_from_address(buf, FALSE);
11394                 }
11395         }
11396         
11397         if (!account && prefs_common.forward_account_autosel) {
11398                 gchar deliveredto[BUFFSIZE];
11399                 if (!procheader_get_header_from_msginfo
11400                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11401                         gchar *buf = deliveredto + strlen("Delivered-To:");
11402                         extract_address(buf);
11403                         account = account_find_from_address(buf, FALSE);
11404                 }
11405         }
11406         
11407         return account;
11408 }
11409
11410 gboolean compose_close(Compose *compose)
11411 {
11412         gint x, y;
11413
11414         if (!g_mutex_trylock(compose->mutex)) {
11415                 /* we have to wait for the (possibly deferred by auto-save)
11416                  * drafting to be done, before destroying the compose under
11417                  * it. */
11418                 debug_print("waiting for drafting to finish...\n");
11419                 compose_allow_user_actions(compose, FALSE);
11420                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11421                 return FALSE;
11422         }
11423         cm_return_val_if_fail(compose, FALSE);
11424         gtkut_widget_get_uposition(compose->window, &x, &y);
11425         if (!compose->batch) {
11426                 prefs_common.compose_x = x;
11427                 prefs_common.compose_y = y;
11428         }
11429         g_mutex_unlock(compose->mutex);
11430         compose_destroy(compose);
11431         return FALSE;
11432 }
11433
11434 /**
11435  * Add entry field for each address in list.
11436  * \param compose     E-Mail composition object.
11437  * \param listAddress List of (formatted) E-Mail addresses.
11438  */
11439 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11440         GList *node;
11441         gchar *addr;
11442         node = listAddress;
11443         while( node ) {
11444                 addr = ( gchar * ) node->data;
11445                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11446                 node = g_list_next( node );
11447         }
11448 }
11449
11450 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11451                                     guint action, gboolean opening_multiple)
11452 {
11453         gchar *body = NULL;
11454         GSList *new_msglist = NULL;
11455         MsgInfo *tmp_msginfo = NULL;
11456         gboolean originally_enc = FALSE;
11457         gboolean originally_sig = FALSE;
11458         Compose *compose = NULL;
11459         gchar *s_system = NULL;
11460
11461         cm_return_if_fail(msgview != NULL);
11462
11463         cm_return_if_fail(msginfo_list != NULL);
11464
11465         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11466                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11467                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11468
11469                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11470                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11471                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11472                                                 orig_msginfo, mimeinfo);
11473                         if (tmp_msginfo != NULL) {
11474                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11475
11476                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11477                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11478                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11479
11480                                 tmp_msginfo->folder = orig_msginfo->folder;
11481                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11482                                 if (orig_msginfo->tags) {
11483                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11484                                         tmp_msginfo->folder->tags_dirty = TRUE;
11485                                 }
11486                         }
11487                 }
11488         }
11489
11490         if (!opening_multiple)
11491                 body = messageview_get_selection(msgview);
11492
11493         if (new_msglist) {
11494                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11495                 procmsg_msginfo_free(tmp_msginfo);
11496                 g_slist_free(new_msglist);
11497         } else
11498                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11499
11500         if (compose && originally_enc) {
11501                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11502         }
11503
11504         if (compose && originally_sig && compose->account->default_sign_reply) {
11505                 compose_force_signing(compose, compose->account, s_system);
11506         }
11507         g_free(s_system);
11508         g_free(body);
11509         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11510 }
11511
11512 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11513                                     guint action)
11514 {
11515         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11516         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11517                 GSList *cur = msginfo_list;
11518                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11519                                                "messages. Opening the windows "
11520                                                "could take some time. Do you "
11521                                                "want to continue?"), 
11522                                                g_slist_length(msginfo_list));
11523                 if (g_slist_length(msginfo_list) > 9
11524                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11525                     != G_ALERTALTERNATE) {
11526                         g_free(msg);
11527                         return;
11528                 }
11529                 g_free(msg);
11530                 /* We'll open multiple compose windows */
11531                 /* let the WM place the next windows */
11532                 compose_force_window_origin = FALSE;
11533                 for (; cur; cur = cur->next) {
11534                         GSList tmplist;
11535                         tmplist.data = cur->data;
11536                         tmplist.next = NULL;
11537                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11538                 }
11539                 compose_force_window_origin = TRUE;
11540         } else {
11541                 /* forwarding multiple mails as attachments is done via a
11542                  * single compose window */
11543                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11544         }
11545 }
11546
11547 void compose_check_for_email_account(Compose *compose)
11548 {
11549         PrefsAccount *ac = NULL, *curr = NULL;
11550         GList *list;
11551         
11552         if (!compose)
11553                 return;
11554
11555         if (compose->account && compose->account->protocol == A_NNTP) {
11556                 ac = account_get_cur_account();
11557                 if (ac->protocol == A_NNTP) {
11558                         list = account_get_list();
11559                         
11560                         for( ; list != NULL ; list = g_list_next(list)) {
11561                                 curr = (PrefsAccount *) list->data;
11562                                 if (curr->protocol != A_NNTP) {
11563                                         ac = curr;
11564                                         break;
11565                                 }
11566                         }
11567                 }
11568                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11569                                         ac->account_id); 
11570         }
11571 }
11572
11573 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11574                                 const gchar *address)
11575 {
11576         GSList *msginfo_list = NULL;
11577         gchar *body =  messageview_get_selection(msgview);
11578         Compose *compose;
11579         
11580         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11581         
11582         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11583         compose_check_for_email_account(compose);
11584         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11585         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11586         compose_reply_set_subject(compose, msginfo);
11587
11588         g_free(body);
11589         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11590 }
11591
11592 void compose_set_position(Compose *compose, gint pos)
11593 {
11594         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11595
11596         gtkut_text_view_set_position(text, pos);
11597 }
11598
11599 gboolean compose_search_string(Compose *compose,
11600                                 const gchar *str, gboolean case_sens)
11601 {
11602         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11603
11604         return gtkut_text_view_search_string(text, str, case_sens);
11605 }
11606
11607 gboolean compose_search_string_backward(Compose *compose,
11608                                 const gchar *str, gboolean case_sens)
11609 {
11610         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11611
11612         return gtkut_text_view_search_string_backward(text, str, case_sens);
11613 }
11614
11615 /* allocate a msginfo structure and populate its data from a compose data structure */
11616 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11617 {
11618         MsgInfo *newmsginfo;
11619         GSList *list;
11620         gchar buf[BUFFSIZE];
11621
11622         cm_return_val_if_fail( compose != NULL, NULL );
11623
11624         newmsginfo = procmsg_msginfo_new();
11625
11626         /* date is now */
11627         get_rfc822_date(buf, sizeof(buf));
11628         newmsginfo->date = g_strdup(buf);
11629
11630         /* from */
11631         if (compose->from_name) {
11632                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11633                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11634         }
11635
11636         /* subject */
11637         if (compose->subject_entry)
11638                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11639
11640         /* to, cc, reply-to, newsgroups */
11641         for (list = compose->header_list; list; list = list->next) {
11642                 gchar *header = gtk_editable_get_chars(
11643                                                                 GTK_EDITABLE(
11644                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11645                 gchar *entry = gtk_editable_get_chars(
11646                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11647
11648                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11649                         if ( newmsginfo->to == NULL ) {
11650                                 newmsginfo->to = g_strdup(entry);
11651                         } else if (entry && *entry) {
11652                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11653                                 g_free(newmsginfo->to);
11654                                 newmsginfo->to = tmp;
11655                         }
11656                 } else
11657                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11658                         if ( newmsginfo->cc == NULL ) {
11659                                 newmsginfo->cc = g_strdup(entry);
11660                         } else if (entry && *entry) {
11661                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11662                                 g_free(newmsginfo->cc);
11663                                 newmsginfo->cc = tmp;
11664                         }
11665                 } else
11666                 if ( strcasecmp(header,
11667                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11668                         if ( newmsginfo->newsgroups == NULL ) {
11669                                 newmsginfo->newsgroups = g_strdup(entry);
11670                         } else if (entry && *entry) {
11671                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11672                                 g_free(newmsginfo->newsgroups);
11673                                 newmsginfo->newsgroups = tmp;
11674                         }
11675                 }
11676
11677                 g_free(header);
11678                 g_free(entry);  
11679         }
11680
11681         /* other data is unset */
11682
11683         return newmsginfo;
11684 }
11685
11686 #ifdef USE_ENCHANT
11687 /* update compose's dictionaries from folder dict settings */
11688 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11689                                                 FolderItem *folder_item)
11690 {
11691         cm_return_if_fail(compose != NULL);
11692
11693         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11694                 FolderItemPrefs *prefs = folder_item->prefs;
11695
11696                 if (prefs->enable_default_dictionary)
11697                         gtkaspell_change_dict(compose->gtkaspell,
11698                                         prefs->default_dictionary, FALSE);
11699                 if (folder_item->prefs->enable_default_alt_dictionary)
11700                         gtkaspell_change_alt_dict(compose->gtkaspell,
11701                                         prefs->default_alt_dictionary);
11702                 if (prefs->enable_default_dictionary
11703                         || prefs->enable_default_alt_dictionary)
11704                         compose_spell_menu_changed(compose);
11705         }
11706 }
11707 #endif
11708
11709 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11710 {
11711         Compose *compose = (Compose *)data;
11712
11713         cm_return_if_fail(compose != NULL);
11714
11715         gtk_widget_grab_focus(compose->text);
11716 }
11717
11718 /*
11719  * End of Source.
11720  */