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