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