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