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