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