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