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