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