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