Same for bsfilter
[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 "base64.h"
88 #include "quoted-printable.h"
89 #include "codeconv.h"
90 #include "utils.h"
91 #include "gtkutils.h"
92 #include "gtkshruler.h"
93 #include "socket.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
96 #include "folder.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
100 #include "undo.h"
101 #include "foldersel.h"
102 #include "toolbar.h"
103 #include "inc.h"
104 #include "message_search.h"
105 #include "combobox.h"
106 #include "hooks.h"
107 #include "privacy.h"
108 #include "timing.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
111
112 enum
113 {
114         COL_MIMETYPE = 0,
115         COL_SIZE     = 1,
116         COL_NAME     = 2,
117         COL_CHARSET  = 3,
118         COL_DATA     = 4,
119         COL_AUTODATA = 5,
120         N_COL_COLUMNS
121 };
122
123 #define N_ATTACH_COLS   (N_COL_COLUMNS)
124
125 typedef enum
126 {
127         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
142
143 typedef enum
144 {
145         PRIORITY_HIGHEST = 1,
146         PRIORITY_HIGH,
147         PRIORITY_NORMAL,
148         PRIORITY_LOW,
149         PRIORITY_LOWEST
150 } PriorityLevel;
151
152 typedef enum
153 {
154         COMPOSE_INSERT_SUCCESS,
155         COMPOSE_INSERT_READ_ERROR,
156         COMPOSE_INSERT_INVALID_CHARACTER,
157         COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
159
160 typedef enum
161 {
162         COMPOSE_WRITE_FOR_SEND,
163         COMPOSE_WRITE_FOR_STORE
164 } ComposeWriteType;
165
166 typedef enum
167 {
168         COMPOSE_QUOTE_FORCED,
169         COMPOSE_QUOTE_CHECK,
170         COMPOSE_QUOTE_SKIP
171 } ComposeQuoteMode;
172
173 typedef enum {
174     TO_FIELD_PRESENT,
175     SUBJECT_FIELD_PRESENT,
176     BODY_FIELD_PRESENT,
177     NO_FIELD_PRESENT
178 } MailField;
179
180 #define B64_LINE_SIZE           57
181 #define B64_BUFFSIZE            77
182
183 #define MAX_REFERENCES_LEN      999
184
185 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
186 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
187
188 static GList *compose_list = NULL;
189 static GSList *extra_headers = NULL;
190
191 static Compose *compose_generic_new                     (PrefsAccount   *account,
192                                                  const gchar    *to,
193                                                  FolderItem     *item,
194                                                  GList          *attach_files,
195                                                  GList          *listAddress );
196
197 static Compose *compose_create                  (PrefsAccount   *account,
198                                                  FolderItem              *item,
199                                                  ComposeMode     mode,
200                                                  gboolean batch);
201
202 static void compose_entry_mark_default_to       (Compose          *compose,
203                                          const gchar      *address);
204 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
205                                          ComposeQuoteMode        quote_mode,
206                                          gboolean        to_all,
207                                          gboolean        to_sender,
208                                          const gchar    *body);
209 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
210                                          GSList         *msginfo_list);
211 static Compose *compose_reply                   (MsgInfo        *msginfo,
212                                          ComposeQuoteMode        quote_mode,
213                                          gboolean        to_all,
214                                          gboolean        to_ml,
215                                          gboolean        to_sender,
216                                          const gchar    *body);
217 static Compose *compose_reply_mode              (ComposeMode     mode, 
218                                          GSList         *msginfo_list, 
219                                          gchar          *body);
220 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
221 static void compose_update_privacy_systems_menu(Compose *compose);
222
223 static GtkWidget *compose_account_option_menu_create
224                                                 (Compose        *compose);
225 static void compose_set_out_encoding            (Compose        *compose);
226 static void compose_set_template_menu           (Compose        *compose);
227 static void compose_destroy                     (Compose        *compose);
228
229 static MailField compose_entries_set            (Compose        *compose,
230                                                  const gchar    *mailto,
231                                                  ComposeEntryType to_type);
232 static gint compose_parse_header                (Compose        *compose,
233                                                  MsgInfo        *msginfo);
234 static gint compose_parse_manual_headers        (Compose        *compose,
235                                                  MsgInfo        *msginfo,
236                                                  HeaderEntry    *entries);
237 static gchar *compose_parse_references          (const gchar    *ref,
238                                                  const gchar    *msgid);
239
240 static gchar *compose_quote_fmt                 (Compose        *compose,
241                                                  MsgInfo        *msginfo,
242                                                  const gchar    *fmt,
243                                                  const gchar    *qmark,
244                                                  const gchar    *body,
245                                                  gboolean        rewrap,
246                                                  gboolean        need_unescape,
247                                                  const gchar *err_msg);
248
249 static void compose_reply_set_entry             (Compose        *compose,
250                                                  MsgInfo        *msginfo,
251                                                  gboolean        to_all,
252                                                  gboolean        to_ml,
253                                                  gboolean        to_sender,
254                                                  gboolean
255                                                  followup_and_reply_to);
256 static void compose_reedit_set_entry            (Compose        *compose,
257                                                  MsgInfo        *msginfo);
258
259 static void compose_insert_sig                  (Compose        *compose,
260                                                  gboolean        replace);
261 static ComposeInsertResult compose_insert_file  (Compose        *compose,
262                                                  const gchar    *file);
263
264 static gboolean compose_attach_append           (Compose        *compose,
265                                                  const gchar    *file,
266                                                  const gchar    *type,
267                                                  const gchar    *content_type,
268                                                  const gchar    *charset);
269 static void compose_attach_parts                (Compose        *compose,
270                                                  MsgInfo        *msginfo);
271
272 static gboolean compose_beautify_paragraph      (Compose        *compose,
273                                                  GtkTextIter    *par_iter,
274                                                  gboolean        force);
275 static void compose_wrap_all                    (Compose        *compose);
276 static void compose_wrap_all_full               (Compose        *compose,
277                                                  gboolean        autowrap);
278
279 static void compose_set_title                   (Compose        *compose);
280 static void compose_select_account              (Compose        *compose,
281                                                  PrefsAccount   *account,
282                                                  gboolean        init);
283
284 static PrefsAccount *compose_current_mail_account(void);
285 /* static gint compose_send                     (Compose        *compose); */
286 static gboolean compose_check_for_valid_recipient
287                                                 (Compose        *compose);
288 static gboolean compose_check_entries           (Compose        *compose,
289                                                  gboolean       check_everything);
290 static gint compose_write_to_file               (Compose        *compose,
291                                                  FILE           *fp,
292                                                  gint            action,
293                                                  gboolean        attach_parts);
294 static gint compose_write_body_to_file          (Compose        *compose,
295                                                  const gchar    *file);
296 static gint compose_remove_reedit_target        (Compose        *compose,
297                                                  gboolean        force);
298 static void compose_remove_draft                        (Compose        *compose);
299 static gint compose_queue_sub                   (Compose        *compose,
300                                                  gint           *msgnum,
301                                                  FolderItem     **item,
302                                                  gchar          **msgpath,
303                                                  gboolean       check_subject,
304                                                  gboolean       remove_reedit_target);
305 static int compose_add_attachments              (Compose        *compose,
306                                                  MimeInfo       *parent);
307 static gchar *compose_get_header                (Compose        *compose);
308 static gchar *compose_get_manual_headers_info   (Compose        *compose);
309
310 static void compose_convert_header              (Compose        *compose,
311                                                  gchar          *dest,
312                                                  gint            len,
313                                                  gchar          *src,
314                                                  gint            header_len,
315                                                  gboolean        addr_field);
316
317 static void compose_attach_info_free            (AttachInfo     *ainfo);
318 static void compose_attach_remove_selected      (GtkAction      *action,
319                                                  gpointer        data);
320
321 static void compose_template_apply              (Compose        *compose,
322                                                  Template       *tmpl,
323                                                  gboolean        replace);
324 static void compose_attach_property             (GtkAction      *action,
325                                                  gpointer        data);
326 static void compose_attach_property_create      (gboolean       *cancelled);
327 static void attach_property_ok                  (GtkWidget      *widget,
328                                                  gboolean       *cancelled);
329 static void attach_property_cancel              (GtkWidget      *widget,
330                                                  gboolean       *cancelled);
331 static gint attach_property_delete_event        (GtkWidget      *widget,
332                                                  GdkEventAny    *event,
333                                                  gboolean       *cancelled);
334 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
335                                                  GdkEventKey    *event,
336                                                  gboolean       *cancelled);
337
338 static void compose_exec_ext_editor             (Compose        *compose);
339 #ifdef G_OS_UNIX
340 static gint compose_exec_ext_editor_real        (const gchar    *file);
341 static gboolean compose_ext_editor_kill         (Compose        *compose);
342 static gboolean compose_input_cb                (GIOChannel     *source,
343                                                  GIOCondition    condition,
344                                                  gpointer        data);
345 static void compose_set_ext_editor_sensitive    (Compose        *compose,
346                                                  gboolean        sensitive);
347 #endif /* G_OS_UNIX */
348
349 static void compose_undo_state_changed          (UndoMain       *undostruct,
350                                                  gint            undo_state,
351                                                  gint            redo_state,
352                                                  gpointer        data);
353
354 static void compose_create_header_entry (Compose *compose);
355 static void compose_add_header_entry    (Compose *compose, const gchar *header,
356                                          gchar *text, ComposePrefType pref_type);
357 static void compose_remove_header_entries(Compose *compose);
358
359 static void compose_update_priority_menu_item(Compose * compose);
360 #if USE_ENCHANT
361 static void compose_spell_menu_changed  (void *data);
362 static void compose_dict_changed        (void *data);
363 #endif
364 static void compose_add_field_list      ( Compose *compose,
365                                           GList *listAddress );
366
367 /* callback functions */
368
369 static void compose_notebook_size_alloc (GtkNotebook *notebook,
370                                          GtkAllocation *allocation,
371                                          GtkPaned *paned);
372 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
373                                          GtkAllocation  *allocation,
374                                          GtkSHRuler     *shruler);
375 static void account_activated           (GtkComboBox *optmenu,
376                                          gpointer        data);
377 static void attach_selected             (GtkTreeView    *tree_view, 
378                                          GtkTreePath    *tree_path,
379                                          GtkTreeViewColumn *column, 
380                                          Compose *compose);
381 static gboolean attach_button_pressed   (GtkWidget      *widget,
382                                          GdkEventButton *event,
383                                          gpointer        data);
384 static gboolean attach_key_pressed      (GtkWidget      *widget,
385                                          GdkEventKey    *event,
386                                          gpointer        data);
387 static void compose_send_cb             (GtkAction      *action, gpointer data);
388 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
389
390 static void compose_save_cb             (GtkAction      *action,
391                                          gpointer        data);
392
393 static void compose_attach_cb           (GtkAction      *action,
394                                          gpointer        data);
395 static void compose_insert_file_cb      (GtkAction      *action,
396                                          gpointer        data);
397 static void compose_insert_sig_cb       (GtkAction      *action,
398                                          gpointer        data);
399 static void compose_replace_sig_cb      (GtkAction      *action,
400                                          gpointer        data);
401
402 static void compose_close_cb            (GtkAction      *action,
403                                          gpointer        data);
404 static void compose_print_cb            (GtkAction      *action,
405                                          gpointer        data);
406
407 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
408
409 static void compose_address_cb          (GtkAction      *action,
410                                          gpointer        data);
411 static void about_show_cb               (GtkAction      *action,
412                                          gpointer        data);
413 static void compose_template_activate_cb(GtkWidget      *widget,
414                                          gpointer        data);
415
416 static void compose_ext_editor_cb       (GtkAction      *action,
417                                          gpointer        data);
418
419 static gint compose_delete_cb           (GtkWidget      *widget,
420                                          GdkEventAny    *event,
421                                          gpointer        data);
422
423 static void compose_undo_cb             (GtkAction      *action,
424                                          gpointer        data);
425 static void compose_redo_cb             (GtkAction      *action,
426                                          gpointer        data);
427 static void compose_cut_cb              (GtkAction      *action,
428                                          gpointer        data);
429 static void compose_copy_cb             (GtkAction      *action,
430                                          gpointer        data);
431 static void compose_paste_cb            (GtkAction      *action,
432                                          gpointer        data);
433 static void compose_paste_as_quote_cb   (GtkAction      *action,
434                                          gpointer        data);
435 static void compose_paste_no_wrap_cb    (GtkAction      *action,
436                                          gpointer        data);
437 static void compose_paste_wrap_cb       (GtkAction      *action,
438                                          gpointer        data);
439 static void compose_allsel_cb           (GtkAction      *action,
440                                          gpointer        data);
441
442 static void compose_advanced_action_cb  (GtkAction      *action,
443                                          gpointer        data);
444
445 static void compose_grab_focus_cb       (GtkWidget      *widget,
446                                          Compose        *compose);
447
448 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
449                                          Compose        *compose);
450
451 static void compose_wrap_cb             (GtkAction      *action,
452                                          gpointer        data);
453 static void compose_wrap_all_cb         (GtkAction      *action,
454                                          gpointer        data);
455 static void compose_find_cb             (GtkAction      *action,
456                                          gpointer        data);
457 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
458                                          gpointer        data);
459 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
460                                          gpointer        data);
461
462 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
463                                          gpointer        data);
464 static void compose_toggle_sign_cb      (GtkToggleAction *action,
465                                          gpointer        data);
466 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
467                                          gpointer        data);
468 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
469 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
470 static void activate_privacy_system     (Compose *compose, 
471                                          PrefsAccount *account,
472                                          gboolean warn);
473 static void compose_use_signing(Compose *compose, gboolean use_signing);
474 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
475 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
476                                          gpointer        data);
477 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
478                                          gpointer        data);
479 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
480 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
481 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
482
483 static void compose_attach_drag_received_cb (GtkWidget          *widget,
484                                              GdkDragContext     *drag_context,
485                                              gint                x,
486                                              gint                y,
487                                              GtkSelectionData   *data,
488                                              guint               info,
489                                              guint               time,
490                                              gpointer            user_data);
491 static void compose_insert_drag_received_cb (GtkWidget          *widget,
492                                              GdkDragContext     *drag_context,
493                                              gint                x,
494                                              gint                y,
495                                              GtkSelectionData   *data,
496                                              guint               info,
497                                              guint               time,
498                                              gpointer            user_data);
499 static void compose_header_drag_received_cb (GtkWidget          *widget,
500                                              GdkDragContext     *drag_context,
501                                              gint                x,
502                                              gint                y,
503                                              GtkSelectionData   *data,
504                                              guint               info,
505                                              guint               time,
506                                              gpointer            user_data);
507
508 static gboolean compose_drag_drop           (GtkWidget *widget,
509                                              GdkDragContext *drag_context,
510                                              gint x, gint y,
511                                              guint time, gpointer user_data);
512 static gboolean completion_set_focus_to_subject
513                                         (GtkWidget    *widget,
514                                          GdkEventKey  *event,
515                                          Compose      *user_data);
516
517 static void text_inserted               (GtkTextBuffer  *buffer,
518                                          GtkTextIter    *iter,
519                                          const gchar    *text,
520                                          gint            len,
521                                          Compose        *compose);
522 static Compose *compose_generic_reply(MsgInfo *msginfo,
523                                   ComposeQuoteMode quote_mode,
524                                   gboolean to_all,
525                                   gboolean to_ml,
526                                   gboolean to_sender,
527                                   gboolean followup_and_reply_to,
528                                   const gchar *body);
529
530 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
531                                             ComposeHeaderEntry *headerentry);
532 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
533                                             GdkEventKey        *event,
534                                             ComposeHeaderEntry *headerentry);
535 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
536                                         ComposeHeaderEntry *headerentry);
537
538 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
539
540 static void compose_allow_user_actions (Compose *compose, gboolean allow);
541
542 static void compose_nothing_cb             (GtkAction *action, gpointer data)
543 {
544
545 }
546
547 #if USE_ENCHANT
548 static void compose_check_all              (GtkAction *action, gpointer data);
549 static void compose_highlight_all          (GtkAction *action, gpointer data);
550 static void compose_check_backwards        (GtkAction *action, gpointer data);
551 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
552 #endif
553
554 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
555
556 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
557
558 #ifdef USE_ENCHANT
559 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
560                                                 FolderItem *folder_item);
561 #endif
562 static void compose_attach_update_label(Compose *compose);
563 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
564                                      gboolean respect_default_to);
565 static void compose_subject_entry_activated(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         GList *l = NULL;
6867         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6868         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6869         g_list_free(l);
6870
6871         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6872                         compose->header_nextrow, compose->header_nextrow+1,
6873                         GTK_SHRINK, GTK_FILL, 0, 0);
6874         if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
6875                 const gchar *last_header_entry = gtk_entry_get_text(
6876                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6877                 string = headers;
6878                 while (*string != NULL) {
6879                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6880                                 standard_header = TRUE;
6881                         string++;
6882                 }
6883                 if (standard_header)
6884                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6885         }
6886         if (!compose->header_last || !standard_header) {
6887                 switch(compose->account->protocol) {
6888                         case A_NNTP:
6889                                 header = prefs_common_translated_header_name("Newsgroups:");
6890                                 break;
6891                         default:
6892                                 header = prefs_common_translated_header_name("To:");
6893                                 break;
6894                 }                                                                   
6895         }
6896         if (header)
6897                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6898
6899         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6900                          G_CALLBACK(compose_grab_focus_cb), compose);
6901
6902         /* Entry field with cleanup button */
6903         button = gtk_button_new();
6904         gtk_button_set_image(GTK_BUTTON(button),
6905                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6906         gtk_widget_show(button);
6907         CLAWS_SET_TIP(button,
6908                 _("Delete entry contents"));
6909         entry = gtk_entry_new(); 
6910         gtk_widget_show(entry);
6911         CLAWS_SET_TIP(entry,
6912                 _("Use <tab> to autocomplete from addressbook"));
6913         hbox = gtk_hbox_new (FALSE, 0);
6914         gtk_widget_show(hbox);
6915         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6916         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6917         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6918                         compose->header_nextrow, compose->header_nextrow+1,
6919                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6920
6921         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6922                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6923                          headerentry);
6924         g_signal_connect(G_OBJECT(entry), "changed", 
6925                          G_CALLBACK(compose_headerentry_changed_cb), 
6926                          headerentry);
6927         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6928                          G_CALLBACK(compose_grab_focus_cb), compose);
6929
6930         g_signal_connect(G_OBJECT(button), "clicked",
6931                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6932                          headerentry); 
6933                          
6934         /* email dnd */
6935         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6936                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6937                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6938         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6939                          G_CALLBACK(compose_header_drag_received_cb),
6940                          entry);
6941         g_signal_connect(G_OBJECT(entry), "drag-drop",
6942                          G_CALLBACK(compose_drag_drop),
6943                          compose);
6944         g_signal_connect(G_OBJECT(entry), "populate-popup",
6945                          G_CALLBACK(compose_entry_popup_extend),
6946                          NULL);
6947         
6948         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6949
6950         headerentry->compose = compose;
6951         headerentry->combo = combo;
6952         headerentry->entry = entry;
6953         headerentry->button = button;
6954         headerentry->hbox = hbox;
6955         headerentry->headernum = compose->header_nextrow;
6956         headerentry->type = PREF_NONE;
6957
6958         compose->header_nextrow++;
6959         compose->header_last = headerentry;             
6960         compose->header_list =
6961                 g_slist_append(compose->header_list,
6962                                headerentry);
6963 }
6964
6965 static void compose_add_header_entry(Compose *compose, const gchar *header,
6966                                 gchar *text, ComposePrefType pref_type) 
6967 {
6968         ComposeHeaderEntry *last_header = compose->header_last;
6969         gchar *tmp = g_strdup(text), *email;
6970         gboolean replyto_hdr;
6971         
6972         replyto_hdr = (!strcasecmp(header,
6973                                 prefs_common_translated_header_name("Reply-To:")) ||
6974                         !strcasecmp(header,
6975                                 prefs_common_translated_header_name("Followup-To:")) ||
6976                         !strcasecmp(header,
6977                                 prefs_common_translated_header_name("In-Reply-To:")));
6978                 
6979         extract_address(tmp);
6980         email = g_utf8_strdown(tmp, -1);
6981         
6982         if (replyto_hdr == FALSE &&
6983             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6984         {
6985                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6986                                 header, text, (gint) pref_type);
6987                 g_free(email);
6988                 g_free(tmp);
6989                 return;
6990         }
6991         
6992         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6993                 gtk_entry_set_text(GTK_ENTRY(
6994                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6995         else
6996                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6997         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6998         last_header->type = pref_type;
6999
7000         if (replyto_hdr == FALSE)
7001                 g_hash_table_insert(compose->email_hashtable, email,
7002                                     GUINT_TO_POINTER(1));
7003         else
7004                 g_free(email);
7005         
7006         g_free(tmp);
7007 }
7008
7009 static void compose_destroy_headerentry(Compose *compose, 
7010                                         ComposeHeaderEntry *headerentry)
7011 {
7012         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7013         gchar *email;
7014
7015         extract_address(text);
7016         email = g_utf8_strdown(text, -1);
7017         g_hash_table_remove(compose->email_hashtable, email);
7018         g_free(text);
7019         g_free(email);
7020         
7021         gtk_widget_destroy(headerentry->combo);
7022         gtk_widget_destroy(headerentry->entry);
7023         gtk_widget_destroy(headerentry->button);
7024         gtk_widget_destroy(headerentry->hbox);
7025         g_free(headerentry);
7026 }
7027
7028 static void compose_remove_header_entries(Compose *compose) 
7029 {
7030         GSList *list;
7031         for (list = compose->header_list; list; list = list->next)
7032                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7033
7034         compose->header_last = NULL;
7035         g_slist_free(compose->header_list);
7036         compose->header_list = NULL;
7037         compose->header_nextrow = 1;
7038         compose_create_header_entry(compose);
7039 }
7040
7041 static GtkWidget *compose_create_header(Compose *compose) 
7042 {
7043         GtkWidget *from_optmenu_hbox;
7044         GtkWidget *header_scrolledwin_main;
7045         GtkWidget *header_table_main;
7046         GtkWidget *header_scrolledwin;
7047         GtkWidget *header_table;
7048
7049         /* parent with account selection and from header */
7050         header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
7051         gtk_widget_show(header_scrolledwin_main);
7052         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7053
7054         header_table_main = gtk_table_new(2, 2, FALSE);
7055         gtk_widget_show(header_table_main);
7056         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7057         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
7058         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
7059
7060         from_optmenu_hbox = compose_account_option_menu_create(compose);
7061         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7062                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7063
7064         /* child with header labels and entries */
7065         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7066         gtk_widget_show(header_scrolledwin);
7067         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7068
7069         header_table = gtk_table_new(2, 2, FALSE);
7070         gtk_widget_show(header_table);
7071         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
7072         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7073         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
7074
7075         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7076                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7077
7078         compose->header_table = header_table;
7079         compose->header_list = NULL;
7080         compose->header_nextrow = 0;
7081
7082         compose_create_header_entry(compose);
7083
7084         compose->table = NULL;
7085
7086         return header_scrolledwin_main;
7087 }
7088
7089 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7090 {
7091         Compose *compose = (Compose *)data;
7092         GdkEventButton event;
7093         
7094         event.button = 3;
7095         event.time = gtk_get_current_event_time();
7096
7097         return attach_button_pressed(compose->attach_clist, &event, compose);
7098 }
7099
7100 static GtkWidget *compose_create_attach(Compose *compose)
7101 {
7102         GtkWidget *attach_scrwin;
7103         GtkWidget *attach_clist;
7104
7105         GtkListStore *store;
7106         GtkCellRenderer *renderer;
7107         GtkTreeViewColumn *column;
7108         GtkTreeSelection *selection;
7109
7110         /* attachment list */
7111         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7112         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7113                                        GTK_POLICY_AUTOMATIC,
7114                                        GTK_POLICY_AUTOMATIC);
7115         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7116
7117         store = gtk_list_store_new(N_ATTACH_COLS, 
7118                                    G_TYPE_STRING,
7119                                    G_TYPE_STRING,
7120                                    G_TYPE_STRING,
7121                                    G_TYPE_STRING,
7122                                    G_TYPE_POINTER,
7123                                    G_TYPE_AUTO_POINTER,
7124                                    -1);
7125         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7126                                         (GTK_TREE_MODEL(store)));
7127         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7128         g_object_unref(store);
7129         
7130         renderer = gtk_cell_renderer_text_new();
7131         column = gtk_tree_view_column_new_with_attributes
7132                         (_("Mime type"), renderer, "text", 
7133                          COL_MIMETYPE, NULL);
7134         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7135         
7136         renderer = gtk_cell_renderer_text_new();
7137         column = gtk_tree_view_column_new_with_attributes
7138                         (_("Size"), renderer, "text", 
7139                          COL_SIZE, NULL);
7140         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7141         
7142         renderer = gtk_cell_renderer_text_new();
7143         column = gtk_tree_view_column_new_with_attributes
7144                         (_("Name"), renderer, "text", 
7145                          COL_NAME, NULL);
7146         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7147
7148         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7149                                      prefs_common.use_stripes_everywhere);
7150         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7151         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7152
7153         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7154                          G_CALLBACK(attach_selected), compose);
7155         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7156                          G_CALLBACK(attach_button_pressed), compose);
7157         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7158                          G_CALLBACK(popup_attach_button_pressed), compose);
7159         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7160                          G_CALLBACK(attach_key_pressed), compose);
7161
7162         /* drag and drop */
7163         gtk_drag_dest_set(attach_clist,
7164                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7165                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7166                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7167         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7168                          G_CALLBACK(compose_attach_drag_received_cb),
7169                          compose);
7170         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7171                          G_CALLBACK(compose_drag_drop),
7172                          compose);
7173
7174         compose->attach_scrwin = attach_scrwin;
7175         compose->attach_clist  = attach_clist;
7176
7177         return attach_scrwin;
7178 }
7179
7180 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7181 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7182
7183 static GtkWidget *compose_create_others(Compose *compose)
7184 {
7185         GtkWidget *table;
7186         GtkWidget *savemsg_checkbtn;
7187         GtkWidget *savemsg_combo;
7188         GtkWidget *savemsg_select;
7189         
7190         guint rowcount = 0;
7191         gchar *folderidentifier;
7192
7193         /* Table for settings */
7194         table = gtk_table_new(3, 1, FALSE);
7195         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7196         gtk_widget_show(table);
7197         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7198         rowcount = 0;
7199
7200         /* Save Message to folder */
7201         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7202         gtk_widget_show(savemsg_checkbtn);
7203         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7204         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7205                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7206         }
7207         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7208                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7209
7210 #if !GTK_CHECK_VERSION(2, 24, 0)
7211         savemsg_combo = gtk_combo_box_entry_new_text();
7212 #else
7213         savemsg_combo = gtk_combo_box_text_new_with_entry();
7214 #endif
7215         compose->savemsg_checkbtn = savemsg_checkbtn;
7216         compose->savemsg_combo = savemsg_combo;
7217         gtk_widget_show(savemsg_combo);
7218
7219         if (prefs_common.compose_save_to_history)
7220 #if !GTK_CHECK_VERSION(2, 24, 0)
7221                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7222                                 prefs_common.compose_save_to_history);
7223 #else
7224                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7225                                 prefs_common.compose_save_to_history);
7226 #endif
7227         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7228         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7229         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7230                          G_CALLBACK(compose_grab_focus_cb), compose);
7231         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7232                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7233                                   (compose->account, F_OUTBOX));
7234                 compose_set_save_to(compose, folderidentifier);
7235                 g_free(folderidentifier);
7236         }
7237
7238         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7239         gtk_widget_show(savemsg_select);
7240         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7241         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7242                          G_CALLBACK(compose_savemsg_select_cb),
7243                          compose);
7244
7245         return table;   
7246 }
7247
7248 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7249 {
7250         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7251                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7252 }
7253
7254 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7255 {
7256         FolderItem *dest;
7257         gchar * path;
7258
7259         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7260         if (!dest) return;
7261
7262         path = folder_item_get_identifier(dest);
7263
7264         compose_set_save_to(compose, path);
7265         g_free(path);
7266 }
7267
7268 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7269                                   GdkAtom clip, GtkTextIter *insert_place);
7270
7271
7272 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7273                                        Compose *compose)
7274 {
7275         gint prev_autowrap;
7276         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7277 #if USE_ENCHANT
7278         if (event->button == 3) {
7279                 GtkTextIter iter;
7280                 GtkTextIter sel_start, sel_end;
7281                 gboolean stuff_selected;
7282                 gint x, y;
7283                 /* move the cursor to allow GtkAspell to check the word
7284                  * under the mouse */
7285                 if (event->x && event->y) {
7286                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7287                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7288                                 &x, &y);
7289                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7290                                 &iter, x, y);
7291                 } else {
7292                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7293                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7294                 }
7295                 /* get selection */
7296                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7297                                 buffer,
7298                                 &sel_start, &sel_end);
7299
7300                 gtk_text_buffer_place_cursor (buffer, &iter);
7301                 /* reselect stuff */
7302                 if (stuff_selected 
7303                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7304                         gtk_text_buffer_select_range(buffer,
7305                                 &sel_start, &sel_end);
7306                 }
7307                 return FALSE; /* pass the event so that the right-click goes through */
7308         }
7309 #endif
7310         if (event->button == 2) {
7311                 GtkTextIter iter;
7312                 gint x, y;
7313                 BLOCK_WRAP();
7314                 
7315                 /* get the middle-click position to paste at the correct place */
7316                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7317                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7318                         &x, &y);
7319                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7320                         &iter, x, y);
7321                 
7322                 entry_paste_clipboard(compose, text, 
7323                                 prefs_common.linewrap_pastes,
7324                                 GDK_SELECTION_PRIMARY, &iter);
7325                 UNBLOCK_WRAP();
7326                 return TRUE;
7327         }
7328         return FALSE;
7329 }
7330
7331 #if USE_ENCHANT
7332 static void compose_spell_menu_changed(void *data)
7333 {
7334         Compose *compose = (Compose *)data;
7335         GSList *items;
7336         GtkWidget *menuitem;
7337         GtkWidget *parent_item;
7338         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7339         GSList *spell_menu;
7340
7341         if (compose->gtkaspell == NULL)
7342                 return;
7343
7344         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7345                         "/Menu/Spelling/Options");
7346
7347         /* setting the submenu removes /Spelling/Options from the factory 
7348          * so we need to save it */
7349
7350         if (parent_item == NULL) {
7351                 parent_item = compose->aspell_options_menu;
7352                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7353         } else
7354                 compose->aspell_options_menu = parent_item;
7355
7356         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7357
7358         spell_menu = g_slist_reverse(spell_menu);
7359         for (items = spell_menu;
7360              items; items = items->next) {
7361                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7362                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7363                 gtk_widget_show(GTK_WIDGET(menuitem));
7364         }
7365         g_slist_free(spell_menu);
7366
7367         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7368         gtk_widget_show(parent_item);
7369 }
7370
7371 static void compose_dict_changed(void *data)
7372 {
7373         Compose *compose = (Compose *) data;
7374
7375         if(!compose->gtkaspell)
7376                 return; 
7377         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7378                 return;
7379
7380         gtkaspell_highlight_all(compose->gtkaspell);
7381         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7382 }
7383 #endif
7384
7385 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7386 {
7387         Compose *compose = (Compose *)data;
7388         GdkEventButton event;
7389         
7390         event.button = 3;
7391         event.time = gtk_get_current_event_time();
7392         event.x = 0;
7393         event.y = 0;
7394
7395         return text_clicked(compose->text, &event, compose);
7396 }
7397
7398 static gboolean compose_force_window_origin = TRUE;
7399 static Compose *compose_create(PrefsAccount *account,
7400                                                  FolderItem *folder,
7401                                                  ComposeMode mode,
7402                                                  gboolean batch)
7403 {
7404         Compose   *compose;
7405         GtkWidget *window;
7406         GtkWidget *vbox;
7407         GtkWidget *menubar;
7408         GtkWidget *handlebox;
7409
7410         GtkWidget *notebook;
7411         
7412         GtkWidget *attach_hbox;
7413         GtkWidget *attach_lab1;
7414         GtkWidget *attach_lab2;
7415
7416         GtkWidget *vbox2;
7417
7418         GtkWidget *label;
7419         GtkWidget *subject_hbox;
7420         GtkWidget *subject_frame;
7421         GtkWidget *subject_entry;
7422         GtkWidget *subject;
7423         GtkWidget *paned;
7424
7425         GtkWidget *edit_vbox;
7426         GtkWidget *ruler_hbox;
7427         GtkWidget *ruler;
7428         GtkWidget *scrolledwin;
7429         GtkWidget *text;
7430         GtkTextBuffer *buffer;
7431         GtkClipboard *clipboard;
7432
7433         UndoMain *undostruct;
7434
7435         GtkWidget *popupmenu;
7436         GtkWidget *tmpl_menu;
7437         GtkActionGroup *action_group = NULL;
7438
7439 #if USE_ENCHANT
7440         GtkAspell * gtkaspell = NULL;
7441 #endif
7442
7443         static GdkGeometry geometry;
7444
7445         cm_return_val_if_fail(account != NULL, NULL);
7446
7447         debug_print("Creating compose window...\n");
7448         compose = g_new0(Compose, 1);
7449
7450         compose->batch = batch;
7451         compose->account = account;
7452         compose->folder = folder;
7453         
7454         compose->mutex = cm_mutex_new();
7455         compose->set_cursor_pos = -1;
7456
7457         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7458
7459         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7460         gtk_widget_set_size_request(window, prefs_common.compose_width,
7461                                         prefs_common.compose_height);
7462
7463         if (!geometry.max_width) {
7464                 geometry.max_width = gdk_screen_width();
7465                 geometry.max_height = gdk_screen_height();
7466         }
7467
7468         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7469                                       &geometry, GDK_HINT_MAX_SIZE);
7470         if (!geometry.min_width) {
7471                 geometry.min_width = 600;
7472                 geometry.min_height = 440;
7473         }
7474         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7475                                       &geometry, GDK_HINT_MIN_SIZE);
7476
7477 #ifndef GENERIC_UMPC    
7478         if (compose_force_window_origin)
7479                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7480                                  prefs_common.compose_y);
7481 #endif
7482         g_signal_connect(G_OBJECT(window), "delete_event",
7483                          G_CALLBACK(compose_delete_cb), compose);
7484         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7485         gtk_widget_realize(window);
7486
7487         gtkut_widget_set_composer_icon(window);
7488
7489         vbox = gtk_vbox_new(FALSE, 0);
7490         gtk_container_add(GTK_CONTAINER(window), vbox);
7491
7492         compose->ui_manager = gtk_ui_manager_new();
7493         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7494                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7495         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7496                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7497         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7498                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7499         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7500                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7501         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7502                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7503
7504         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7505
7506         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7507         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7508 #ifdef USE_ENCHANT
7509         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7510 #endif
7511         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7512         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7513         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7514
7515 /* Compose menu */
7516         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7517         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7518         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7519         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7520         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7521         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7522         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7523         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7524         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7525         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7526         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7527         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7528         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7529
7530 /* Edit menu */
7531         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7532         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7533         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7534
7535         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7536         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7537         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7538
7539         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7540         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7541         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7542         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7543
7544         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7545
7546         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7547         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7548         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7549         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7550         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7551         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7552         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7553         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7554         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7555         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7556         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7557         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7558         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7559         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7560         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7561
7562         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7563
7564         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7565         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7566         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7567         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7568         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7569
7570         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7571
7572         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7573
7574 #if USE_ENCHANT
7575 /* Spelling menu */
7576         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7577         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7578         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7579         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7580         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7581         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7582 #endif
7583
7584 /* Options menu */
7585         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7586         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7587         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7588         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7589         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7590
7591         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7592         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7593         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7594         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7595         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7596
7597         
7598         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7599         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7600         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7601         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7602         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7603         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7604         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7605
7606         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7607         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7608         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7609         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7610         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7611
7612         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7613
7614         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7615         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7616         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7617         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7618         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7619
7620         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7621         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)
7622         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)
7623         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7624
7625         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7626
7627         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7628         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)
7629         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)
7630
7631         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7632
7633         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7634         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)
7635         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7636
7637         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7638         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)
7639         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7640
7641         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7642
7643         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7644         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)
7645         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7646         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7647         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7648
7649         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7650         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)
7651         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)
7652         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7653         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7654
7655         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7656         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7657         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7658         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7659         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7660         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7661
7662         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7663         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7664         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)
7665
7666         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7667         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7668         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7669 /* phew. */
7670
7671 /* Tools menu */
7672         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7673         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7674         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7675         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7676         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7677         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7678
7679 /* Help menu */
7680         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7681
7682         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7683         gtk_widget_show_all(menubar);
7684
7685         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7686         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7687
7688         if (prefs_common.toolbar_detachable) {
7689                 handlebox = gtk_handle_box_new();
7690         } else {
7691                 handlebox = gtk_hbox_new(FALSE, 0);
7692         }
7693         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7694
7695         gtk_widget_realize(handlebox);
7696         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7697                                           (gpointer)compose);
7698
7699         vbox2 = gtk_vbox_new(FALSE, 2);
7700         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7701         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7702         
7703         /* Notebook */
7704         notebook = gtk_notebook_new();
7705         gtk_widget_show(notebook);
7706
7707         /* header labels and entries */
7708         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7709                         compose_create_header(compose),
7710                         gtk_label_new_with_mnemonic(_("Hea_der")));
7711         /* attachment list */
7712         attach_hbox = gtk_hbox_new(FALSE, 0);
7713         gtk_widget_show(attach_hbox);
7714         
7715         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7716         gtk_widget_show(attach_lab1);
7717         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7718         
7719         attach_lab2 = gtk_label_new("");
7720         gtk_widget_show(attach_lab2);
7721         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7722         
7723         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7724                         compose_create_attach(compose),
7725                         attach_hbox);
7726         /* Others Tab */
7727         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7728                         compose_create_others(compose),
7729                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7730
7731         /* Subject */
7732         subject_hbox = gtk_hbox_new(FALSE, 0);
7733         gtk_widget_show(subject_hbox);
7734
7735         subject_frame = gtk_frame_new(NULL);
7736         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7737         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7738         gtk_widget_show(subject_frame);
7739
7740         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7741         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7742         gtk_widget_show(subject);
7743
7744         label = gtk_label_new_with_mnemonic(_("Subject:"));
7745         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7746         gtk_widget_show(label);
7747
7748 #ifdef USE_ENCHANT
7749         subject_entry = claws_spell_entry_new();
7750 #else
7751         subject_entry = gtk_entry_new();
7752 #endif
7753         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7754         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7755                          G_CALLBACK(compose_grab_focus_cb), compose);
7756         gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
7757         gtk_widget_show(subject_entry);
7758         compose->subject_entry = subject_entry;
7759         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7760         
7761         edit_vbox = gtk_vbox_new(FALSE, 0);
7762
7763         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7764
7765         /* ruler */
7766         ruler_hbox = gtk_hbox_new(FALSE, 0);
7767         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7768
7769         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7770         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7771         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7772                            BORDER_WIDTH);
7773
7774         /* text widget */
7775         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7776         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7777                                        GTK_POLICY_AUTOMATIC,
7778                                        GTK_POLICY_AUTOMATIC);
7779         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7780                                             GTK_SHADOW_IN);
7781         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7782
7783         text = gtk_text_view_new();
7784         if (prefs_common.show_compose_margin) {
7785                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7786                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7787         }
7788         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7789         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7790         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7791         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7792         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7793         
7794         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7795         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7796                                G_CALLBACK(compose_edit_size_alloc),
7797                                ruler);
7798         g_signal_connect(G_OBJECT(buffer), "changed",
7799                          G_CALLBACK(compose_changed_cb), compose);
7800         g_signal_connect(G_OBJECT(text), "grab_focus",
7801                          G_CALLBACK(compose_grab_focus_cb), compose);
7802         g_signal_connect(G_OBJECT(buffer), "insert_text",
7803                          G_CALLBACK(text_inserted), compose);
7804         g_signal_connect(G_OBJECT(text), "button_press_event",
7805                          G_CALLBACK(text_clicked), compose);
7806         g_signal_connect(G_OBJECT(text), "popup-menu",
7807                          G_CALLBACK(compose_popup_menu), compose);
7808         g_signal_connect(G_OBJECT(subject_entry), "changed",
7809                         G_CALLBACK(compose_changed_cb), compose);
7810         g_signal_connect(G_OBJECT(subject_entry), "activate",
7811                         G_CALLBACK(compose_subject_entry_activated), compose);
7812
7813         /* drag and drop */
7814         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7815                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7816                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7817         g_signal_connect(G_OBJECT(text), "drag_data_received",
7818                          G_CALLBACK(compose_insert_drag_received_cb),
7819                          compose);
7820         g_signal_connect(G_OBJECT(text), "drag-drop",
7821                          G_CALLBACK(compose_drag_drop),
7822                          compose);
7823         g_signal_connect(G_OBJECT(text), "key-press-event",
7824                          G_CALLBACK(completion_set_focus_to_subject),
7825                          compose);
7826         gtk_widget_show_all(vbox);
7827
7828         /* pane between attach clist and text */
7829         paned = gtk_vpaned_new();
7830         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7831         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
7832         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
7833         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
7834         g_signal_connect(G_OBJECT(notebook), "size_allocate",
7835                          G_CALLBACK(compose_notebook_size_alloc), paned);
7836
7837         gtk_widget_show_all(paned);
7838
7839
7840         if (prefs_common.textfont) {
7841                 PangoFontDescription *font_desc;
7842
7843                 font_desc = pango_font_description_from_string
7844                         (prefs_common.textfont);
7845                 if (font_desc) {
7846                         gtk_widget_modify_font(text, font_desc);
7847                         pango_font_description_free(font_desc);
7848                 }
7849         }
7850
7851         gtk_action_group_add_actions(action_group, compose_popup_entries,
7852                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7853         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7854         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7855         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7856         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7857         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7858         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7859         
7860         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7861
7862         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7863         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7864         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7865
7866         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7867
7868         undostruct = undo_init(text);
7869         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7870                                    compose);
7871
7872         address_completion_start(window);
7873
7874         compose->window        = window;
7875         compose->vbox          = vbox;
7876         compose->menubar       = menubar;
7877         compose->handlebox     = handlebox;
7878
7879         compose->vbox2         = vbox2;
7880
7881         compose->paned = paned;
7882
7883         compose->attach_label  = attach_lab2;
7884
7885         compose->notebook      = notebook;
7886         compose->edit_vbox     = edit_vbox;
7887         compose->ruler_hbox    = ruler_hbox;
7888         compose->ruler         = ruler;
7889         compose->scrolledwin   = scrolledwin;
7890         compose->text          = text;
7891
7892         compose->focused_editable = NULL;
7893
7894         compose->popupmenu    = popupmenu;
7895
7896         compose->tmpl_menu = tmpl_menu;
7897
7898         compose->mode = mode;
7899         compose->rmode = mode;
7900
7901         compose->targetinfo = NULL;
7902         compose->replyinfo  = NULL;
7903         compose->fwdinfo    = NULL;
7904
7905         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7906                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7907         
7908         compose->replyto     = NULL;
7909         compose->cc          = NULL;
7910         compose->bcc         = NULL;
7911         compose->followup_to = NULL;
7912
7913         compose->ml_post     = NULL;
7914
7915         compose->inreplyto   = NULL;
7916         compose->references  = NULL;
7917         compose->msgid       = NULL;
7918         compose->boundary    = NULL;
7919
7920         compose->autowrap       = prefs_common.autowrap;
7921         compose->autoindent     = prefs_common.auto_indent;
7922         compose->use_signing    = FALSE;
7923         compose->use_encryption = FALSE;
7924         compose->privacy_system = NULL;
7925
7926         compose->modified = FALSE;
7927
7928         compose->return_receipt = FALSE;
7929
7930         compose->to_list        = NULL;
7931         compose->newsgroup_list = NULL;
7932
7933         compose->undostruct = undostruct;
7934
7935         compose->sig_str = NULL;
7936
7937         compose->exteditor_file    = NULL;
7938         compose->exteditor_pid     = -1;
7939         compose->exteditor_tag     = -1;
7940         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
7941
7942         compose->folder_update_callback_id =
7943                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
7944                                 compose_update_folder_hook,
7945                                 (gpointer) compose);
7946
7947 #if USE_ENCHANT
7948         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7949         if (mode != COMPOSE_REDIRECT) {
7950                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7951                     strcmp(prefs_common.dictionary, "")) {
7952                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7953                                                   prefs_common.alt_dictionary,
7954                                                   conv_get_locale_charset_str(),
7955                                                   prefs_common.misspelled_col,
7956                                                   prefs_common.check_while_typing,
7957                                                   prefs_common.recheck_when_changing_dict,
7958                                                   prefs_common.use_alternate,
7959                                                   prefs_common.use_both_dicts,
7960                                                   GTK_TEXT_VIEW(text),
7961                                                   GTK_WINDOW(compose->window),
7962                                                   compose_dict_changed,
7963                                                   compose_spell_menu_changed,
7964                                                   compose);
7965                         if (!gtkaspell) {
7966                                 alertpanel_error(_("Spell checker could not "
7967                                                 "be started.\n%s"),
7968                                                 gtkaspell_checkers_strerror());
7969                                 gtkaspell_checkers_reset_error();
7970                         } else {
7971                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7972                         }
7973                 }
7974         }
7975         compose->gtkaspell = gtkaspell;
7976         compose_spell_menu_changed(compose);
7977         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7978 #endif
7979
7980         compose_select_account(compose, account, TRUE);
7981
7982         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7983         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7984
7985         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7986                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7987
7988         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7989                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7990         
7991         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7992                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7993
7994         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7995         if (account->protocol != A_NNTP)
7996                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7997                                 prefs_common_translated_header_name("To:"));
7998         else
7999                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8000                                 prefs_common_translated_header_name("Newsgroups:"));
8001
8002 #ifndef USE_NEW_ADDRBOOK
8003         addressbook_set_target_compose(compose);
8004 #endif  
8005         if (mode != COMPOSE_REDIRECT)
8006                 compose_set_template_menu(compose);
8007         else {
8008                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8009         }
8010
8011         compose_list = g_list_append(compose_list, compose);
8012
8013         if (!prefs_common.show_ruler)
8014                 gtk_widget_hide(ruler_hbox);
8015                 
8016         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8017
8018         /* Priority */
8019         compose->priority = PRIORITY_NORMAL;
8020         compose_update_priority_menu_item(compose);
8021
8022         compose_set_out_encoding(compose);
8023         
8024         /* Actions menu */
8025         compose_update_actions_menu(compose);
8026
8027         /* Privacy Systems menu */
8028         compose_update_privacy_systems_menu(compose);
8029
8030         activate_privacy_system(compose, account, TRUE);
8031         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8032         if (batch) {
8033                 gtk_widget_realize(window);
8034         } else {
8035                 gtk_widget_show(window);
8036         }
8037         
8038         return compose;
8039 }
8040
8041 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8042 {
8043         GList *accounts;
8044         GtkWidget *hbox;
8045         GtkWidget *optmenu;
8046         GtkWidget *optmenubox;
8047         GtkListStore *menu;
8048         GtkTreeIter iter;
8049         GtkWidget *from_name = NULL;
8050
8051         gint num = 0, def_menu = 0;
8052         
8053         accounts = account_get_list();
8054         cm_return_val_if_fail(accounts != NULL, NULL);
8055
8056         optmenubox = gtk_event_box_new();
8057         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8058         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8059
8060         hbox = gtk_hbox_new(FALSE, 6);
8061         from_name = gtk_entry_new();
8062         
8063         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8064                          G_CALLBACK(compose_grab_focus_cb), compose);
8065
8066         for (; accounts != NULL; accounts = accounts->next, num++) {
8067                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8068                 gchar *name, *from = NULL;
8069
8070                 if (ac == compose->account) def_menu = num;
8071
8072                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
8073                                        ac->account_name);
8074                 
8075                 if (ac == compose->account) {
8076                         if (ac->name && *ac->name) {
8077                                 gchar *buf;
8078                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8079                                 from = g_strdup_printf("%s <%s>",
8080                                                        buf, ac->address);
8081                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8082                         } else {
8083                                 from = g_strdup_printf("%s",
8084                                                        ac->address);
8085                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8086                         }
8087                 }
8088                 COMBOBOX_ADD(menu, name, ac->account_id);
8089                 g_free(name);
8090                 g_free(from);
8091         }
8092
8093         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8094
8095         g_signal_connect(G_OBJECT(optmenu), "changed",
8096                         G_CALLBACK(account_activated),
8097                         compose);
8098         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8099                          G_CALLBACK(compose_entry_popup_extend),
8100                          NULL);
8101
8102         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8103         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8104         
8105         CLAWS_SET_TIP(optmenubox,
8106                 _("Account to use for this email"));
8107         CLAWS_SET_TIP(from_name,
8108                 _("Sender address to be used"));
8109
8110         compose->account_combo = optmenu;
8111         compose->from_name = from_name;
8112         
8113         return hbox;
8114 }
8115
8116 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8117 {
8118         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8119         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8120         Compose *compose = (Compose *) data;
8121         if (active) {
8122                 compose->priority = value;
8123         }
8124 }
8125
8126 static void compose_reply_change_mode(Compose *compose,
8127                                     ComposeMode action)
8128 {
8129         gboolean was_modified = compose->modified;
8130
8131         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8132         
8133         cm_return_if_fail(compose->replyinfo != NULL);
8134         
8135         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8136                 ml = TRUE;
8137         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8138                 followup = TRUE;
8139         if (action == COMPOSE_REPLY_TO_ALL)
8140                 all = TRUE;
8141         if (action == COMPOSE_REPLY_TO_SENDER)
8142                 sender = TRUE;
8143         if (action == COMPOSE_REPLY_TO_LIST)
8144                 ml = TRUE;
8145
8146         compose_remove_header_entries(compose);
8147         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8148         if (compose->account->set_autocc && compose->account->auto_cc)
8149                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8150
8151         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8152                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8153         
8154         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8155                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8156         compose_show_first_last_header(compose, TRUE);
8157         compose->modified = was_modified;
8158         compose_set_title(compose);
8159 }
8160
8161 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8162 {
8163         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8164         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8165         Compose *compose = (Compose *) data;
8166         
8167         if (active)
8168                 compose_reply_change_mode(compose, value);
8169 }
8170
8171 static void compose_update_priority_menu_item(Compose * compose)
8172 {
8173         GtkWidget *menuitem = NULL;
8174         switch (compose->priority) {
8175                 case PRIORITY_HIGHEST:
8176                         menuitem = gtk_ui_manager_get_widget
8177                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8178                         break;
8179                 case PRIORITY_HIGH:
8180                         menuitem = gtk_ui_manager_get_widget
8181                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8182                         break;
8183                 case PRIORITY_NORMAL:
8184                         menuitem = gtk_ui_manager_get_widget
8185                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8186                         break;
8187                 case PRIORITY_LOW:
8188                         menuitem = gtk_ui_manager_get_widget
8189                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8190                         break;
8191                 case PRIORITY_LOWEST:
8192                         menuitem = gtk_ui_manager_get_widget
8193                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8194                         break;
8195         }
8196         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8197 }       
8198
8199 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8200 {
8201         Compose *compose = (Compose *) data;
8202         gchar *systemid;
8203         gboolean can_sign = FALSE, can_encrypt = FALSE;
8204
8205         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8206
8207         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8208                 return;
8209
8210         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8211         g_free(compose->privacy_system);
8212         compose->privacy_system = NULL;
8213         if (systemid != NULL) {
8214                 compose->privacy_system = g_strdup(systemid);
8215
8216                 can_sign = privacy_system_can_sign(systemid);
8217                 can_encrypt = privacy_system_can_encrypt(systemid);
8218         }
8219
8220         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8221
8222         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8223         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8224 }
8225
8226 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8227 {
8228         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8229         GtkWidget *menuitem = NULL;
8230         GList *children, *amenu;
8231         gboolean can_sign = FALSE, can_encrypt = FALSE;
8232         gboolean found = FALSE;
8233
8234         if (compose->privacy_system != NULL) {
8235                 gchar *systemid;
8236                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8237                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8238                 cm_return_if_fail(menuitem != NULL);
8239
8240                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8241                 amenu = children;
8242                 menuitem = NULL;
8243                 while (amenu != NULL) {
8244                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8245                         if (systemid != NULL) {
8246                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8247                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8248                                         menuitem = GTK_WIDGET(amenu->data);
8249
8250                                         can_sign = privacy_system_can_sign(systemid);
8251                                         can_encrypt = privacy_system_can_encrypt(systemid);
8252                                         found = TRUE;
8253                                         break;
8254                                 } 
8255                         } else if (strlen(compose->privacy_system) == 0 && 
8256                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8257                                         menuitem = GTK_WIDGET(amenu->data);
8258
8259                                         can_sign = FALSE;
8260                                         can_encrypt = FALSE;
8261                                         found = TRUE;
8262                                         break;
8263                         }
8264
8265                         amenu = amenu->next;
8266                 }
8267                 g_list_free(children);
8268                 if (menuitem != NULL)
8269                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8270                 
8271                 if (warn && !found && strlen(compose->privacy_system)) {
8272                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8273                                   "will not be able to sign or encrypt this message."),
8274                                   compose->privacy_system);
8275                 }
8276         } 
8277
8278         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8279         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8280 }       
8281  
8282 static void compose_set_out_encoding(Compose *compose)
8283 {
8284         CharSet out_encoding;
8285         const gchar *branch = NULL;
8286         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8287
8288         switch(out_encoding) {
8289                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8290                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8291                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8292                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8293                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8294                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8295                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8296                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8297                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8298                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8299                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8300                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8301                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8302                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8303                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8304                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8305                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8306                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8307                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8308                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8309                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8310                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8311                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8312                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8313                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8314                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8315                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8316                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8317                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8318                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8319                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8320                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8321                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8322         }
8323         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8324 }
8325
8326 static void compose_set_template_menu(Compose *compose)
8327 {
8328         GSList *tmpl_list, *cur;
8329         GtkWidget *menu;
8330         GtkWidget *item;
8331
8332         tmpl_list = template_get_config();
8333
8334         menu = gtk_menu_new();
8335
8336         gtk_menu_set_accel_group (GTK_MENU (menu), 
8337                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8338         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8339                 Template *tmpl = (Template *)cur->data;
8340                 gchar *accel_path = NULL;
8341                 item = gtk_menu_item_new_with_label(tmpl->name);
8342                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8343                 g_signal_connect(G_OBJECT(item), "activate",
8344                                  G_CALLBACK(compose_template_activate_cb),
8345                                  compose);
8346                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8347                 gtk_widget_show(item);
8348                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8349                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8350                 g_free(accel_path);
8351         }
8352
8353         gtk_widget_show(menu);
8354         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8355 }
8356
8357 void compose_update_actions_menu(Compose *compose)
8358 {
8359         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8360 }
8361
8362 static void compose_update_privacy_systems_menu(Compose *compose)
8363 {
8364         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8365         GSList *systems, *cur;
8366         GtkWidget *widget;
8367         GtkWidget *system_none;
8368         GSList *group;
8369         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8370         GtkWidget *privacy_menu = gtk_menu_new();
8371
8372         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8373         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8374
8375         g_signal_connect(G_OBJECT(system_none), "activate",
8376                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8377
8378         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8379         gtk_widget_show(system_none);
8380
8381         systems = privacy_get_system_ids();
8382         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8383                 gchar *systemid = cur->data;
8384
8385                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8386                 widget = gtk_radio_menu_item_new_with_label(group,
8387                         privacy_system_get_name(systemid));
8388                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8389                                        g_strdup(systemid), g_free);
8390                 g_signal_connect(G_OBJECT(widget), "activate",
8391                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8392
8393                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8394                 gtk_widget_show(widget);
8395                 g_free(systemid);
8396         }
8397         g_slist_free(systems);
8398         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8399         gtk_widget_show_all(privacy_menu);
8400         gtk_widget_show_all(privacy_menuitem);
8401 }
8402
8403 void compose_reflect_prefs_all(void)
8404 {
8405         GList *cur;
8406         Compose *compose;
8407
8408         for (cur = compose_list; cur != NULL; cur = cur->next) {
8409                 compose = (Compose *)cur->data;
8410                 compose_set_template_menu(compose);
8411         }
8412 }
8413
8414 void compose_reflect_prefs_pixmap_theme(void)
8415 {
8416         GList *cur;
8417         Compose *compose;
8418
8419         for (cur = compose_list; cur != NULL; cur = cur->next) {
8420                 compose = (Compose *)cur->data;
8421                 toolbar_update(TOOLBAR_COMPOSE, compose);
8422         }
8423 }
8424
8425 static const gchar *compose_quote_char_from_context(Compose *compose)
8426 {
8427         const gchar *qmark = NULL;
8428
8429         cm_return_val_if_fail(compose != NULL, NULL);
8430
8431         switch (compose->mode) {
8432                 /* use forward-specific quote char */
8433                 case COMPOSE_FORWARD:
8434                 case COMPOSE_FORWARD_AS_ATTACH:
8435                 case COMPOSE_FORWARD_INLINE:
8436                         if (compose->folder && compose->folder->prefs &&
8437                                         compose->folder->prefs->forward_with_format)
8438                                 qmark = compose->folder->prefs->forward_quotemark;
8439                         else if (compose->account->forward_with_format)
8440                                 qmark = compose->account->forward_quotemark;
8441                         else
8442                                 qmark = prefs_common.fw_quotemark;
8443                         break;
8444
8445                 /* use reply-specific quote char in all other modes */
8446                 default:
8447                         if (compose->folder && compose->folder->prefs &&
8448                                         compose->folder->prefs->reply_with_format)
8449                                 qmark = compose->folder->prefs->reply_quotemark;
8450                         else if (compose->account->reply_with_format)
8451                                 qmark = compose->account->reply_quotemark;
8452                         else
8453                                 qmark = prefs_common.quotemark;
8454                         break;
8455         }
8456
8457         if (qmark == NULL || *qmark == '\0')
8458                 qmark = "> ";
8459
8460         return qmark;
8461 }
8462
8463 static void compose_template_apply(Compose *compose, Template *tmpl,
8464                                    gboolean replace)
8465 {
8466         GtkTextView *text;
8467         GtkTextBuffer *buffer;
8468         GtkTextMark *mark;
8469         GtkTextIter iter;
8470         const gchar *qmark;
8471         gchar *parsed_str = NULL;
8472         gint cursor_pos = 0;
8473         const gchar *err_msg = _("The body of the template has an error at line %d.");
8474         if (!tmpl) return;
8475
8476         /* process the body */
8477
8478         text = GTK_TEXT_VIEW(compose->text);
8479         buffer = gtk_text_view_get_buffer(text);
8480
8481         if (tmpl->value) {
8482                 qmark = compose_quote_char_from_context(compose);
8483
8484                 if (compose->replyinfo != NULL) {
8485
8486                         if (replace)
8487                                 gtk_text_buffer_set_text(buffer, "", -1);
8488                         mark = gtk_text_buffer_get_insert(buffer);
8489                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8490
8491                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8492                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8493
8494                 } else if (compose->fwdinfo != NULL) {
8495
8496                         if (replace)
8497                                 gtk_text_buffer_set_text(buffer, "", -1);
8498                         mark = gtk_text_buffer_get_insert(buffer);
8499                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8500
8501                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8502                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8503
8504                 } else {
8505                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8506
8507                         GtkTextIter start, end;
8508                         gchar *tmp = NULL;
8509
8510                         gtk_text_buffer_get_start_iter(buffer, &start);
8511                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8512                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8513
8514                         /* clear the buffer now */
8515                         if (replace)
8516                                 gtk_text_buffer_set_text(buffer, "", -1);
8517
8518                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8519                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8520                         procmsg_msginfo_free( dummyinfo );
8521
8522                         g_free( tmp );
8523                 } 
8524         } else {
8525                 if (replace)
8526                         gtk_text_buffer_set_text(buffer, "", -1);
8527                 mark = gtk_text_buffer_get_insert(buffer);
8528                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8529         }       
8530
8531         if (replace && parsed_str && compose->account->auto_sig)
8532                 compose_insert_sig(compose, FALSE);
8533
8534         if (replace && parsed_str) {
8535                 gtk_text_buffer_get_start_iter(buffer, &iter);
8536                 gtk_text_buffer_place_cursor(buffer, &iter);
8537         }
8538         
8539         if (parsed_str) {
8540                 cursor_pos = quote_fmt_get_cursor_pos();
8541                 compose->set_cursor_pos = cursor_pos;
8542                 if (cursor_pos == -1)
8543                         cursor_pos = 0;
8544                 gtk_text_buffer_get_start_iter(buffer, &iter);
8545                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8546                 gtk_text_buffer_place_cursor(buffer, &iter);
8547         }
8548
8549         /* process the other fields */
8550
8551         compose_template_apply_fields(compose, tmpl);
8552         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8553         quote_fmt_reset_vartable();
8554         compose_changed_cb(NULL, compose);
8555
8556 #ifdef USE_ENCHANT
8557         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8558                 gtkaspell_highlight_all(compose->gtkaspell);
8559 #endif
8560 }
8561
8562 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8563 {
8564         MsgInfo* dummyinfo = NULL;
8565         MsgInfo *msginfo = NULL;
8566         gchar *buf = NULL;
8567
8568         if (compose->replyinfo != NULL)
8569                 msginfo = compose->replyinfo;
8570         else if (compose->fwdinfo != NULL)
8571                 msginfo = compose->fwdinfo;
8572         else {
8573                 dummyinfo = compose_msginfo_new_from_compose(compose);
8574                 msginfo = dummyinfo;
8575         }
8576
8577         if (tmpl->from && *tmpl->from != '\0') {
8578 #ifdef USE_ENCHANT
8579                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8580                                 compose->gtkaspell);
8581 #else
8582                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8583 #endif
8584                 quote_fmt_scan_string(tmpl->from);
8585                 quote_fmt_parse();
8586
8587                 buf = quote_fmt_get_buffer();
8588                 if (buf == NULL) {
8589                         alertpanel_error(_("Template From format error."));
8590                 } else {
8591                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8592                 }
8593         }
8594
8595         if (tmpl->to && *tmpl->to != '\0') {
8596 #ifdef USE_ENCHANT
8597                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8598                                 compose->gtkaspell);
8599 #else
8600                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8601 #endif
8602                 quote_fmt_scan_string(tmpl->to);
8603                 quote_fmt_parse();
8604
8605                 buf = quote_fmt_get_buffer();
8606                 if (buf == NULL) {
8607                         alertpanel_error(_("Template To format error."));
8608                 } else {
8609                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8610                 }
8611         }
8612
8613         if (tmpl->cc && *tmpl->cc != '\0') {
8614 #ifdef USE_ENCHANT
8615                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8616                                 compose->gtkaspell);
8617 #else
8618                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8619 #endif
8620                 quote_fmt_scan_string(tmpl->cc);
8621                 quote_fmt_parse();
8622
8623                 buf = quote_fmt_get_buffer();
8624                 if (buf == NULL) {
8625                         alertpanel_error(_("Template Cc format error."));
8626                 } else {
8627                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8628                 }
8629         }
8630
8631         if (tmpl->bcc && *tmpl->bcc != '\0') {
8632 #ifdef USE_ENCHANT
8633                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8634                                 compose->gtkaspell);
8635 #else
8636                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8637 #endif
8638                 quote_fmt_scan_string(tmpl->bcc);
8639                 quote_fmt_parse();
8640
8641                 buf = quote_fmt_get_buffer();
8642                 if (buf == NULL) {
8643                         alertpanel_error(_("Template Bcc format error."));
8644                 } else {
8645                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8646                 }
8647         }
8648
8649         /* process the subject */
8650         if (tmpl->subject && *tmpl->subject != '\0') {
8651 #ifdef USE_ENCHANT
8652                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8653                                 compose->gtkaspell);
8654 #else
8655                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8656 #endif
8657                 quote_fmt_scan_string(tmpl->subject);
8658                 quote_fmt_parse();
8659
8660                 buf = quote_fmt_get_buffer();
8661                 if (buf == NULL) {
8662                         alertpanel_error(_("Template subject format error."));
8663                 } else {
8664                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8665                 }
8666         }
8667
8668         procmsg_msginfo_free( dummyinfo );
8669 }
8670
8671 static void compose_destroy(Compose *compose)
8672 {
8673         GtkAllocation allocation;
8674         GtkTextBuffer *buffer;
8675         GtkClipboard *clipboard;
8676
8677         compose_list = g_list_remove(compose_list, compose);
8678
8679         if (compose->updating) {
8680                 debug_print("danger, not destroying anything now\n");
8681                 compose->deferred_destroy = TRUE;
8682                 return;
8683         }
8684
8685         /* NOTE: address_completion_end() does nothing with the window
8686          * however this may change. */
8687         address_completion_end(compose->window);
8688
8689         slist_free_strings_full(compose->to_list);
8690         slist_free_strings_full(compose->newsgroup_list);
8691         slist_free_strings_full(compose->header_list);
8692
8693         slist_free_strings_full(extra_headers);
8694         extra_headers = NULL;
8695
8696         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8697
8698         g_hash_table_destroy(compose->email_hashtable);
8699
8700         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8701                         compose->folder_update_callback_id);
8702
8703         procmsg_msginfo_free(compose->targetinfo);
8704         procmsg_msginfo_free(compose->replyinfo);
8705         procmsg_msginfo_free(compose->fwdinfo);
8706
8707         g_free(compose->replyto);
8708         g_free(compose->cc);
8709         g_free(compose->bcc);
8710         g_free(compose->newsgroups);
8711         g_free(compose->followup_to);
8712
8713         g_free(compose->ml_post);
8714
8715         g_free(compose->inreplyto);
8716         g_free(compose->references);
8717         g_free(compose->msgid);
8718         g_free(compose->boundary);
8719
8720         g_free(compose->redirect_filename);
8721         if (compose->undostruct)
8722                 undo_destroy(compose->undostruct);
8723
8724         g_free(compose->sig_str);
8725
8726         g_free(compose->exteditor_file);
8727
8728         g_free(compose->orig_charset);
8729
8730         g_free(compose->privacy_system);
8731
8732 #ifndef USE_NEW_ADDRBOOK
8733         if (addressbook_get_target_compose() == compose)
8734                 addressbook_set_target_compose(NULL);
8735 #endif
8736 #if USE_ENCHANT
8737         if (compose->gtkaspell) {
8738                 gtkaspell_delete(compose->gtkaspell);
8739                 compose->gtkaspell = NULL;
8740         }
8741 #endif
8742
8743         if (!compose->batch) {
8744                 gtk_widget_get_allocation(compose->window, &allocation);
8745                 prefs_common.compose_width = allocation.width;
8746                 prefs_common.compose_height = allocation.height;
8747         }
8748
8749         if (!gtk_widget_get_parent(compose->paned))
8750                 gtk_widget_destroy(compose->paned);
8751         gtk_widget_destroy(compose->popupmenu);
8752
8753         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8754         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8755         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8756
8757         gtk_widget_destroy(compose->window);
8758         toolbar_destroy(compose->toolbar);
8759         g_free(compose->toolbar);
8760         cm_mutex_free(compose->mutex);
8761         g_free(compose);
8762 }
8763
8764 static void compose_attach_info_free(AttachInfo *ainfo)
8765 {
8766         g_free(ainfo->file);
8767         g_free(ainfo->content_type);
8768         g_free(ainfo->name);
8769         g_free(ainfo->charset);
8770         g_free(ainfo);
8771 }
8772
8773 static void compose_attach_update_label(Compose *compose)
8774 {
8775         GtkTreeIter iter;
8776         gint i = 1;
8777         gchar *text;
8778         GtkTreeModel *model;
8779         
8780         if(compose == NULL)
8781                 return;
8782                 
8783         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8784         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8785                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8786                 return;
8787         }
8788         
8789         while(gtk_tree_model_iter_next(model, &iter))
8790                 i++;
8791         
8792         text = g_strdup_printf("(%d)", i);
8793         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8794         g_free(text);
8795 }
8796
8797 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8798 {
8799         Compose *compose = (Compose *)data;
8800         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8801         GtkTreeSelection *selection;
8802         GList *sel, *cur;
8803         GtkTreeModel *model;
8804
8805         selection = gtk_tree_view_get_selection(tree_view);
8806         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8807
8808         if (!sel) 
8809                 return;
8810
8811         for (cur = sel; cur != NULL; cur = cur->next) {
8812                 GtkTreePath *path = cur->data;
8813                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8814                                                 (model, cur->data);
8815                 cur->data = ref;
8816                 gtk_tree_path_free(path);
8817         }
8818
8819         for (cur = sel; cur != NULL; cur = cur->next) {
8820                 GtkTreeRowReference *ref = cur->data;
8821                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8822                 GtkTreeIter iter;
8823
8824                 if (gtk_tree_model_get_iter(model, &iter, path))
8825                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8826                 
8827                 gtk_tree_path_free(path);
8828                 gtk_tree_row_reference_free(ref);
8829         }
8830
8831         g_list_free(sel);
8832         compose_attach_update_label(compose);
8833 }
8834
8835 static struct _AttachProperty
8836 {
8837         GtkWidget *window;
8838         GtkWidget *mimetype_entry;
8839         GtkWidget *encoding_optmenu;
8840         GtkWidget *path_entry;
8841         GtkWidget *filename_entry;
8842         GtkWidget *ok_btn;
8843         GtkWidget *cancel_btn;
8844 } attach_prop;
8845
8846 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8847 {       
8848         gtk_tree_path_free((GtkTreePath *)ptr);
8849 }
8850
8851 static void compose_attach_property(GtkAction *action, gpointer data)
8852 {
8853         Compose *compose = (Compose *)data;
8854         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8855         AttachInfo *ainfo;
8856         GtkComboBox *optmenu;
8857         GtkTreeSelection *selection;
8858         GList *sel;
8859         GtkTreeModel *model;
8860         GtkTreeIter iter;
8861         GtkTreePath *path;
8862         static gboolean cancelled;
8863
8864         /* only if one selected */
8865         selection = gtk_tree_view_get_selection(tree_view);
8866         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8867                 return;
8868
8869         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8870         if (!sel)
8871                 return;
8872
8873         path = (GtkTreePath *) sel->data;
8874         gtk_tree_model_get_iter(model, &iter, path);
8875         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8876         
8877         if (!ainfo) {
8878                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8879                 g_list_free(sel);
8880                 return;
8881         }               
8882         g_list_free(sel);
8883
8884         if (!attach_prop.window)
8885                 compose_attach_property_create(&cancelled);
8886         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8887         gtk_widget_grab_focus(attach_prop.ok_btn);
8888         gtk_widget_show(attach_prop.window);
8889         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8890                         GTK_WINDOW(compose->window));
8891
8892         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8893         if (ainfo->encoding == ENC_UNKNOWN)
8894                 combobox_select_by_data(optmenu, ENC_BASE64);
8895         else
8896                 combobox_select_by_data(optmenu, ainfo->encoding);
8897
8898         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8899                            ainfo->content_type ? ainfo->content_type : "");
8900         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8901                            ainfo->file ? ainfo->file : "");
8902         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8903                            ainfo->name ? ainfo->name : "");
8904
8905         for (;;) {
8906                 const gchar *entry_text;
8907                 gchar *text;
8908                 gchar *cnttype = NULL;
8909                 gchar *file = NULL;
8910                 off_t size = 0;
8911
8912                 cancelled = FALSE;
8913                 gtk_main();
8914
8915                 gtk_widget_hide(attach_prop.window);
8916                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8917                 
8918                 if (cancelled)
8919                         break;
8920
8921                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8922                 if (*entry_text != '\0') {
8923                         gchar *p;
8924
8925                         text = g_strstrip(g_strdup(entry_text));
8926                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8927                                 cnttype = g_strdup(text);
8928                                 g_free(text);
8929                         } else {
8930                                 alertpanel_error(_("Invalid MIME type."));
8931                                 g_free(text);
8932                                 continue;
8933                         }
8934                 }
8935
8936                 ainfo->encoding = combobox_get_active_data(optmenu);
8937
8938                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8939                 if (*entry_text != '\0') {
8940                         if (is_file_exist(entry_text) &&
8941                             (size = get_file_size(entry_text)) > 0)
8942                                 file = g_strdup(entry_text);
8943                         else {
8944                                 alertpanel_error
8945                                         (_("File doesn't exist or is empty."));
8946                                 g_free(cnttype);
8947                                 continue;
8948                         }
8949                 }
8950
8951                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8952                 if (*entry_text != '\0') {
8953                         g_free(ainfo->name);
8954                         ainfo->name = g_strdup(entry_text);
8955                 }
8956
8957                 if (cnttype) {
8958                         g_free(ainfo->content_type);
8959                         ainfo->content_type = cnttype;
8960                 }
8961                 if (file) {
8962                         g_free(ainfo->file);
8963                         ainfo->file = file;
8964                 }
8965                 if (size)
8966                         ainfo->size = (goffset)size;
8967
8968                 /* update tree store */
8969                 text = to_human_readable(ainfo->size);
8970                 gtk_tree_model_get_iter(model, &iter, path);
8971                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8972                                    COL_MIMETYPE, ainfo->content_type,
8973                                    COL_SIZE, text,
8974                                    COL_NAME, ainfo->name,
8975                                    COL_CHARSET, ainfo->charset,
8976                                    -1);
8977                 
8978                 break;
8979         }
8980
8981         gtk_tree_path_free(path);
8982 }
8983
8984 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8985 { \
8986         label = gtk_label_new(str); \
8987         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8988                          GTK_FILL, 0, 0, 0); \
8989         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8990  \
8991         entry = gtk_entry_new(); \
8992         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8993                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8994 }
8995
8996 static void compose_attach_property_create(gboolean *cancelled)
8997 {
8998         GtkWidget *window;
8999         GtkWidget *vbox;
9000         GtkWidget *table;
9001         GtkWidget *label;
9002         GtkWidget *mimetype_entry;
9003         GtkWidget *hbox;
9004         GtkWidget *optmenu;
9005         GtkListStore *optmenu_menu;
9006         GtkWidget *path_entry;
9007         GtkWidget *filename_entry;
9008         GtkWidget *hbbox;
9009         GtkWidget *ok_btn;
9010         GtkWidget *cancel_btn;
9011         GList     *mime_type_list, *strlist;
9012         GtkTreeIter iter;
9013
9014         debug_print("Creating attach_property window...\n");
9015
9016         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9017         gtk_widget_set_size_request(window, 480, -1);
9018         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9019         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9020         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9021         g_signal_connect(G_OBJECT(window), "delete_event",
9022                          G_CALLBACK(attach_property_delete_event),
9023                          cancelled);
9024         g_signal_connect(G_OBJECT(window), "key_press_event",
9025                          G_CALLBACK(attach_property_key_pressed),
9026                          cancelled);
9027
9028         vbox = gtk_vbox_new(FALSE, 8);
9029         gtk_container_add(GTK_CONTAINER(window), vbox);
9030
9031         table = gtk_table_new(4, 2, FALSE);
9032         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9033         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9034         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9035
9036         label = gtk_label_new(_("MIME type")); 
9037         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9038                          GTK_FILL, 0, 0, 0); 
9039         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9040 #if !GTK_CHECK_VERSION(2, 24, 0)
9041         mimetype_entry = gtk_combo_box_entry_new_text(); 
9042 #else
9043         mimetype_entry = gtk_combo_box_text_new_with_entry();
9044 #endif
9045         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9046                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9047                          
9048         /* stuff with list */
9049         mime_type_list = procmime_get_mime_type_list();
9050         strlist = NULL;
9051         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9052                 MimeType *type = (MimeType *) mime_type_list->data;
9053                 gchar *tmp;
9054
9055                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9056
9057                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9058                         g_free(tmp);
9059                 else
9060                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9061                                         (GCompareFunc)strcmp2);
9062         }
9063
9064         for (mime_type_list = strlist; mime_type_list != NULL; 
9065                 mime_type_list = mime_type_list->next) {
9066 #if !GTK_CHECK_VERSION(2, 24, 0)
9067                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9068 #else
9069                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9070 #endif
9071                 g_free(mime_type_list->data);
9072         }
9073         g_list_free(strlist);
9074         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9075         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9076
9077         label = gtk_label_new(_("Encoding"));
9078         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9079                          GTK_FILL, 0, 0, 0);
9080         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9081
9082         hbox = gtk_hbox_new(FALSE, 0);
9083         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9084                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9085
9086         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9087         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9088
9089         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9090         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9091         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9092         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9093         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9094
9095         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9096
9097         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9098         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9099
9100         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9101                                       &ok_btn, GTK_STOCK_OK,
9102                                       NULL, NULL);
9103         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9104         gtk_widget_grab_default(ok_btn);
9105
9106         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9107                          G_CALLBACK(attach_property_ok),
9108                          cancelled);
9109         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9110                          G_CALLBACK(attach_property_cancel),
9111                          cancelled);
9112
9113         gtk_widget_show_all(vbox);
9114
9115         attach_prop.window           = window;
9116         attach_prop.mimetype_entry   = mimetype_entry;
9117         attach_prop.encoding_optmenu = optmenu;
9118         attach_prop.path_entry       = path_entry;
9119         attach_prop.filename_entry   = filename_entry;
9120         attach_prop.ok_btn           = ok_btn;
9121         attach_prop.cancel_btn       = cancel_btn;
9122 }
9123
9124 #undef SET_LABEL_AND_ENTRY
9125
9126 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9127 {
9128         *cancelled = FALSE;
9129         gtk_main_quit();
9130 }
9131
9132 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9133 {
9134         *cancelled = TRUE;
9135         gtk_main_quit();
9136 }
9137
9138 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9139                                          gboolean *cancelled)
9140 {
9141         *cancelled = TRUE;
9142         gtk_main_quit();
9143
9144         return TRUE;
9145 }
9146
9147 static gboolean attach_property_key_pressed(GtkWidget *widget,
9148                                             GdkEventKey *event,
9149                                             gboolean *cancelled)
9150 {
9151         if (event && event->keyval == GDK_KEY_Escape) {
9152                 *cancelled = TRUE;
9153                 gtk_main_quit();
9154         }
9155         if (event && event->keyval == GDK_KEY_Return) {
9156                 *cancelled = FALSE;
9157                 gtk_main_quit();
9158                 return TRUE;
9159         }
9160         return FALSE;
9161 }
9162
9163 static void compose_exec_ext_editor(Compose *compose)
9164 {
9165 #ifdef G_OS_UNIX
9166         gchar *tmp;
9167         pid_t pid;
9168         gint pipe_fds[2];
9169
9170         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9171                               G_DIR_SEPARATOR, compose);
9172
9173         if (pipe(pipe_fds) < 0) {
9174                 perror("pipe");
9175                 g_free(tmp);
9176                 return;
9177         }
9178
9179         if ((pid = fork()) < 0) {
9180                 perror("fork");
9181                 g_free(tmp);
9182                 return;
9183         }
9184
9185         if (pid != 0) {
9186                 /* close the write side of the pipe */
9187                 close(pipe_fds[1]);
9188
9189                 compose->exteditor_file    = g_strdup(tmp);
9190                 compose->exteditor_pid     = pid;
9191
9192                 compose_set_ext_editor_sensitive(compose, FALSE);
9193
9194 #ifndef G_OS_WIN32
9195                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9196 #else
9197                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9198 #endif
9199                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9200                                                         G_IO_IN,
9201                                                         compose_input_cb,
9202                                                         compose);
9203         } else {        /* process-monitoring process */
9204                 pid_t pid_ed;
9205
9206                 if (setpgid(0, 0))
9207                         perror("setpgid");
9208
9209                 /* close the read side of the pipe */
9210                 close(pipe_fds[0]);
9211
9212                 if (compose_write_body_to_file(compose, tmp) < 0) {
9213                         fd_write_all(pipe_fds[1], "2\n", 2);
9214                         _exit(1);
9215                 }
9216
9217                 pid_ed = compose_exec_ext_editor_real(tmp);
9218                 if (pid_ed < 0) {
9219                         fd_write_all(pipe_fds[1], "1\n", 2);
9220                         _exit(1);
9221                 }
9222
9223                 /* wait until editor is terminated */
9224                 waitpid(pid_ed, NULL, 0);
9225
9226                 fd_write_all(pipe_fds[1], "0\n", 2);
9227
9228                 close(pipe_fds[1]);
9229                 _exit(0);
9230         }
9231
9232         g_free(tmp);
9233 #endif /* G_OS_UNIX */
9234 }
9235
9236 #ifdef G_OS_UNIX
9237 static gint compose_exec_ext_editor_real(const gchar *file)
9238 {
9239         gchar buf[1024];
9240         gchar *p;
9241         gchar **cmdline;
9242         pid_t pid;
9243
9244         cm_return_val_if_fail(file != NULL, -1);
9245
9246         if ((pid = fork()) < 0) {
9247                 perror("fork");
9248                 return -1;
9249         }
9250
9251         if (pid != 0) return pid;
9252
9253         /* grandchild process */
9254
9255         if (setpgid(0, getppid()))
9256                 perror("setpgid");
9257
9258         if (prefs_common_get_ext_editor_cmd() &&
9259             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9260             *(p + 1) == 's' && !strchr(p + 2, '%')) {
9261                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9262         } else {
9263                 if (prefs_common_get_ext_editor_cmd())
9264                         g_warning("External editor command-line is invalid: '%s'\n",
9265                                   prefs_common_get_ext_editor_cmd());
9266                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9267         }
9268
9269         cmdline = strsplit_with_quote(buf, " ", 1024);
9270         execvp(cmdline[0], cmdline);
9271
9272         perror("execvp");
9273         g_strfreev(cmdline);
9274
9275         _exit(1);
9276 }
9277
9278 static gboolean compose_ext_editor_kill(Compose *compose)
9279 {
9280         pid_t pgid = compose->exteditor_pid * -1;
9281         gint ret;
9282
9283         ret = kill(pgid, 0);
9284
9285         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9286                 AlertValue val;
9287                 gchar *msg;
9288
9289                 msg = g_strdup_printf
9290                         (_("The external editor is still working.\n"
9291                            "Force terminating the process?\n"
9292                            "process group id: %d"), -pgid);
9293                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9294                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9295                         
9296                 g_free(msg);
9297
9298                 if (val == G_ALERTALTERNATE) {
9299                         g_source_remove(compose->exteditor_tag);
9300                         g_io_channel_shutdown(compose->exteditor_ch,
9301                                               FALSE, NULL);
9302                         g_io_channel_unref(compose->exteditor_ch);
9303
9304                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9305                         waitpid(compose->exteditor_pid, NULL, 0);
9306
9307                         g_warning("Terminated process group id: %d", -pgid);
9308                         g_warning("Temporary file: %s",
9309                                   compose->exteditor_file);
9310
9311                         compose_set_ext_editor_sensitive(compose, TRUE);
9312
9313                         g_free(compose->exteditor_file);
9314                         compose->exteditor_file    = NULL;
9315                         compose->exteditor_pid     = -1;
9316                         compose->exteditor_ch      = NULL;
9317                         compose->exteditor_tag     = -1;
9318                 } else
9319                         return FALSE;
9320         }
9321
9322         return TRUE;
9323 }
9324
9325 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9326                                  gpointer data)
9327 {
9328         gchar buf[3] = "3";
9329         Compose *compose = (Compose *)data;
9330         gsize bytes_read;
9331
9332         debug_print("Compose: input from monitoring process\n");
9333
9334         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9335
9336         g_io_channel_shutdown(source, FALSE, NULL);
9337         g_io_channel_unref(source);
9338
9339         waitpid(compose->exteditor_pid, NULL, 0);
9340
9341         if (buf[0] == '0') {            /* success */
9342                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9343                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9344
9345                 gtk_text_buffer_set_text(buffer, "", -1);
9346                 compose_insert_file(compose, compose->exteditor_file);
9347                 compose_changed_cb(NULL, compose);
9348                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9349
9350                 if (claws_unlink(compose->exteditor_file) < 0)
9351                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9352         } else if (buf[0] == '1') {     /* failed */
9353                 g_warning("Couldn't exec external editor\n");
9354                 if (claws_unlink(compose->exteditor_file) < 0)
9355                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9356         } else if (buf[0] == '2') {
9357                 g_warning("Couldn't write to file\n");
9358         } else if (buf[0] == '3') {
9359                 g_warning("Pipe read failed\n");
9360         }
9361
9362         compose_set_ext_editor_sensitive(compose, TRUE);
9363
9364         g_free(compose->exteditor_file);
9365         compose->exteditor_file    = NULL;
9366         compose->exteditor_pid     = -1;
9367         compose->exteditor_ch      = NULL;
9368         compose->exteditor_tag     = -1;
9369
9370         return FALSE;
9371 }
9372
9373 static void compose_set_ext_editor_sensitive(Compose *compose,
9374                                              gboolean sensitive)
9375 {
9376         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9377         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9378         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9379         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9380         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", sensitive);
9381         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9382         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9383         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9384
9385         gtk_widget_set_sensitive(compose->text,                       sensitive);
9386         if (compose->toolbar->send_btn)
9387                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9388         if (compose->toolbar->sendl_btn)
9389                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9390         if (compose->toolbar->draft_btn)
9391                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9392         if (compose->toolbar->insert_btn)
9393                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9394         if (compose->toolbar->sig_btn)
9395                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9396         if (compose->toolbar->exteditor_btn)
9397                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9398         if (compose->toolbar->linewrap_current_btn)
9399                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9400         if (compose->toolbar->linewrap_all_btn)
9401                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9402 }
9403 #endif /* G_OS_UNIX */
9404
9405 /**
9406  * compose_undo_state_changed:
9407  *
9408  * Change the sensivity of the menuentries undo and redo
9409  **/
9410 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9411                                        gint redo_state, gpointer data)
9412 {
9413         Compose *compose = (Compose *)data;
9414
9415         switch (undo_state) {
9416         case UNDO_STATE_TRUE:
9417                 if (!undostruct->undo_state) {
9418                         undostruct->undo_state = TRUE;
9419                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9420                 }
9421                 break;
9422         case UNDO_STATE_FALSE:
9423                 if (undostruct->undo_state) {
9424                         undostruct->undo_state = FALSE;
9425                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9426                 }
9427                 break;
9428         case UNDO_STATE_UNCHANGED:
9429                 break;
9430         case UNDO_STATE_REFRESH:
9431                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9432                 break;
9433         default:
9434                 g_warning("Undo state not recognized");
9435                 break;
9436         }
9437
9438         switch (redo_state) {
9439         case UNDO_STATE_TRUE:
9440                 if (!undostruct->redo_state) {
9441                         undostruct->redo_state = TRUE;
9442                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9443                 }
9444                 break;
9445         case UNDO_STATE_FALSE:
9446                 if (undostruct->redo_state) {
9447                         undostruct->redo_state = FALSE;
9448                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9449                 }
9450                 break;
9451         case UNDO_STATE_UNCHANGED:
9452                 break;
9453         case UNDO_STATE_REFRESH:
9454                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9455                 break;
9456         default:
9457                 g_warning("Redo state not recognized");
9458                 break;
9459         }
9460 }
9461
9462 /* callback functions */
9463
9464 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9465                                         GtkAllocation *allocation,
9466                                         GtkPaned *paned)
9467 {
9468         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9469 }
9470
9471 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9472  * includes "non-client" (windows-izm) in calculation, so this calculation
9473  * may not be accurate.
9474  */
9475 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9476                                         GtkAllocation *allocation,
9477                                         GtkSHRuler *shruler)
9478 {
9479         if (prefs_common.show_ruler) {
9480                 gint char_width = 0, char_height = 0;
9481                 gint line_width_in_chars;
9482
9483                 gtkut_get_font_size(GTK_WIDGET(widget),
9484                                     &char_width, &char_height);
9485                 line_width_in_chars =
9486                         (allocation->width - allocation->x) / char_width;
9487
9488                 /* got the maximum */
9489                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9490                                     0.0, line_width_in_chars, 0);
9491         }
9492
9493         return TRUE;
9494 }
9495
9496 typedef struct {
9497         gchar                   *header;
9498         gchar                   *entry;
9499         ComposePrefType         type;
9500         gboolean                entry_marked;
9501 } HeaderEntryState;
9502
9503 static void account_activated(GtkComboBox *optmenu, gpointer data)
9504 {
9505         Compose *compose = (Compose *)data;
9506
9507         PrefsAccount *ac;
9508         gchar *folderidentifier;
9509         gint account_id = 0;
9510         GtkTreeModel *menu;
9511         GtkTreeIter iter;
9512         GSList *list, *saved_list = NULL;
9513         HeaderEntryState *state;
9514         GtkRcStyle *style = NULL;
9515 #if !GTK_CHECK_VERSION(3, 0, 0)
9516         static GdkColor yellow;
9517         static gboolean color_set = FALSE;
9518 #else
9519         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9520 #endif
9521
9522         /* Get ID of active account in the combo box */
9523         menu = gtk_combo_box_get_model(optmenu);
9524         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9525         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9526
9527         ac = account_find_from_id(account_id);
9528         cm_return_if_fail(ac != NULL);
9529
9530         if (ac != compose->account) {
9531                 compose_select_account(compose, ac, FALSE);
9532
9533                 for (list = compose->header_list; list; list = list->next) {
9534                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9535                         
9536                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9537                                 compose_destroy_headerentry(compose, hentry);
9538                                 continue;
9539                         }
9540                         
9541                         state = g_malloc0(sizeof(HeaderEntryState));
9542                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9543                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9544                         state->entry = gtk_editable_get_chars(
9545                                         GTK_EDITABLE(hentry->entry), 0, -1);
9546                         state->type = hentry->type;
9547                                 
9548 #if !GTK_CHECK_VERSION(3, 0, 0)
9549                         if (!color_set) {
9550                                 gdk_color_parse("#f5f6be", &yellow);
9551                                 color_set = gdk_colormap_alloc_color(
9552                                                         gdk_colormap_get_system(),
9553                                                         &yellow, FALSE, TRUE);
9554                         }
9555 #endif
9556                                 
9557                         style = gtk_widget_get_modifier_style(hentry->entry);
9558                         state->entry_marked = gdk_color_equal(&yellow,
9559                                                 &style->base[GTK_STATE_NORMAL]);
9560
9561                         saved_list = g_slist_append(saved_list, state);
9562                         compose_destroy_headerentry(compose, hentry);
9563                 }
9564
9565                 compose->header_last = NULL;
9566                 g_slist_free(compose->header_list);
9567                 compose->header_list = NULL;
9568                 compose->header_nextrow = 1;
9569                 compose_create_header_entry(compose);
9570                 
9571                 if (ac->set_autocc && ac->auto_cc)
9572                         compose_entry_append(compose, ac->auto_cc,
9573                                                 COMPOSE_CC, PREF_ACCOUNT);
9574
9575                 if (ac->set_autobcc && ac->auto_bcc) 
9576                         compose_entry_append(compose, ac->auto_bcc,
9577                                                 COMPOSE_BCC, PREF_ACCOUNT);
9578         
9579                 if (ac->set_autoreplyto && ac->auto_replyto)
9580                         compose_entry_append(compose, ac->auto_replyto,
9581                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9582                 
9583                 for (list = saved_list; list; list = list->next) {
9584                         state = (HeaderEntryState *) list->data;
9585                         
9586                         compose_add_header_entry(compose, state->header,
9587                                                 state->entry, state->type);
9588                         if (state->entry_marked)
9589                                 compose_entry_mark_default_to(compose, state->entry);
9590                         
9591                         g_free(state->header);  
9592                         g_free(state->entry);
9593                         g_free(state);
9594                 }
9595                 g_slist_free(saved_list);
9596                 
9597                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9598                                         (ac->protocol == A_NNTP) ? 
9599                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9600         }
9601
9602         /* Set message save folder */
9603         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9604                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9605         }
9606         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9607                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9608                            
9609         compose_set_save_to(compose, NULL);
9610         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9611                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9612                                   (compose->account, F_OUTBOX));
9613                 compose_set_save_to(compose, folderidentifier);
9614                 g_free(folderidentifier);
9615         }
9616 }
9617
9618 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9619                             GtkTreeViewColumn *column, Compose *compose)
9620 {
9621         compose_attach_property(NULL, compose);
9622 }
9623
9624 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9625                                       gpointer data)
9626 {
9627         Compose *compose = (Compose *)data;
9628         GtkTreeSelection *attach_selection;
9629         gint attach_nr_selected;
9630         
9631         if (!event) return FALSE;
9632
9633         if (event->button == 3) {
9634                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9635                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9636                         
9637                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9638                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9639                         
9640                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9641                                NULL, NULL, event->button, event->time);
9642                 return TRUE;                           
9643         }
9644
9645         return FALSE;
9646 }
9647
9648 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9649                                    gpointer data)
9650 {
9651         Compose *compose = (Compose *)data;
9652
9653         if (!event) return FALSE;
9654
9655         switch (event->keyval) {
9656         case GDK_KEY_Delete:
9657                 compose_attach_remove_selected(NULL, compose);
9658                 break;
9659         }
9660         return FALSE;
9661 }
9662
9663 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9664 {
9665         toolbar_comp_set_sensitive(compose, allow);
9666         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9667         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9668 #if USE_ENCHANT
9669         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9670 #endif  
9671         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9672         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9673         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9674         
9675         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9676
9677 }
9678
9679 static void compose_send_cb(GtkAction *action, gpointer data)
9680 {
9681         Compose *compose = (Compose *)data;
9682
9683         if (prefs_common.work_offline && 
9684             !inc_offline_should_override(TRUE,
9685                 _("Claws Mail needs network access in order "
9686                   "to send this email.")))
9687                 return;
9688         
9689         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9690                 g_source_remove(compose->draft_timeout_tag);
9691                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
9692         }
9693
9694         compose_send(compose);
9695 }
9696
9697 static void compose_send_later_cb(GtkAction *action, gpointer data)
9698 {
9699         Compose *compose = (Compose *)data;
9700         gint val;
9701
9702         inc_lock();
9703         compose_allow_user_actions(compose, FALSE);
9704         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9705         compose_allow_user_actions(compose, TRUE);
9706         inc_unlock();
9707
9708         if (!val) {
9709                 compose_close(compose);
9710         } else if (val == -1) {
9711                 alertpanel_error(_("Could not queue message."));
9712         } else if (val == -2) {
9713                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9714         } else if (val == -3) {
9715                 if (privacy_peek_error())
9716                 alertpanel_error(_("Could not queue message for sending:\n\n"
9717                                    "Signature failed: %s"), privacy_get_error());
9718         } else if (val == -4) {
9719                 alertpanel_error(_("Could not queue message for sending:\n\n"
9720                                    "Charset conversion failed."));
9721         } else if (val == -5) {
9722                 alertpanel_error(_("Could not queue message for sending:\n\n"
9723                                    "Couldn't get recipient encryption key."));
9724         } else if (val == -6) {
9725                 /* silent error */
9726         }
9727         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9728 }
9729
9730 #define DRAFTED_AT_EXIT "drafted_at_exit"
9731 static void compose_register_draft(MsgInfo *info)
9732 {
9733         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9734                                       DRAFTED_AT_EXIT, NULL);
9735         FILE *fp = g_fopen(filepath, "ab");
9736         
9737         if (fp) {
9738                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9739                                 info->msgnum);
9740                 fclose(fp);
9741         }
9742                 
9743         g_free(filepath);       
9744 }
9745
9746 gboolean compose_draft (gpointer data, guint action) 
9747 {
9748         Compose *compose = (Compose *)data;
9749         FolderItem *draft;
9750         gchar *tmp;
9751         gchar *sheaders;
9752         gint msgnum;
9753         MsgFlags flag = {0, 0};
9754         static gboolean lock = FALSE;
9755         MsgInfo *newmsginfo;
9756         FILE *fp;
9757         gboolean target_locked = FALSE;
9758         gboolean err = FALSE;
9759
9760         if (lock) return FALSE;
9761
9762         if (compose->sending)
9763                 return TRUE;
9764
9765         draft = account_get_special_folder(compose->account, F_DRAFT);
9766         cm_return_val_if_fail(draft != NULL, FALSE);
9767         
9768         if (!g_mutex_trylock(compose->mutex)) {
9769                 /* we don't want to lock the mutex once it's available,
9770                  * because as the only other part of compose.c locking
9771                  * it is compose_close - which means once unlocked,
9772                  * the compose struct will be freed */
9773                 debug_print("couldn't lock mutex, probably sending\n");
9774                 return FALSE;
9775         }
9776
9777         lock = TRUE;
9778
9779         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9780                               G_DIR_SEPARATOR, compose);
9781         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9782                 FILE_OP_ERROR(tmp, "fopen");
9783                 goto warn_err;
9784         }
9785
9786         /* chmod for security */
9787         if (change_file_mode_rw(fp, tmp) < 0) {
9788                 FILE_OP_ERROR(tmp, "chmod");
9789                 g_warning("can't change file mode\n");
9790         }
9791
9792         /* Save draft infos */
9793         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9794         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9795
9796         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9797                 gchar *savefolderid;
9798
9799                 savefolderid = compose_get_save_to(compose);
9800                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9801                 g_free(savefolderid);
9802         }
9803         if (compose->return_receipt) {
9804                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9805         }
9806         if (compose->privacy_system) {
9807                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9808                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9809                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9810         }
9811
9812         /* Message-ID of message replying to */
9813         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9814                 gchar *folderid = NULL;
9815
9816                 if (compose->replyinfo->folder)
9817                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
9818                 if (folderid == NULL)
9819                         folderid = g_strdup("NULL");
9820
9821                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9822                 g_free(folderid);
9823         }
9824         /* Message-ID of message forwarding to */
9825         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9826                 gchar *folderid = NULL;
9827
9828                 if (compose->fwdinfo->folder)
9829                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9830                 if (folderid == NULL)
9831                         folderid = g_strdup("NULL");
9832
9833                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9834                 g_free(folderid);
9835         }
9836
9837         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9838         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9839
9840         sheaders = compose_get_manual_headers_info(compose);
9841         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9842         g_free(sheaders);
9843
9844         /* end of headers */
9845         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9846
9847         if (err) {
9848                 fclose(fp);
9849                 goto warn_err;
9850         }
9851
9852         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9853                 fclose(fp);
9854                 goto warn_err;
9855         }
9856         if (fclose(fp) == EOF) {
9857                 goto warn_err;
9858         }
9859         
9860         flag.perm_flags = MSG_NEW|MSG_UNREAD;
9861         if (compose->targetinfo) {
9862                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9863                 if (target_locked) 
9864                         flag.perm_flags |= MSG_LOCKED;
9865         }
9866         flag.tmp_flags = MSG_DRAFT;
9867
9868         folder_item_scan(draft);
9869         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9870                 MsgInfo *tmpinfo = NULL;
9871                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9872                 if (compose->msgid) {
9873                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9874                 }
9875                 if (tmpinfo) {
9876                         msgnum = tmpinfo->msgnum;
9877                         procmsg_msginfo_free(tmpinfo);
9878                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9879                 } else {
9880                         debug_print("didn't get draft msgnum after scanning\n");
9881                 }
9882         } else {
9883                 debug_print("got draft msgnum %d from adding\n", msgnum);
9884         }
9885         if (msgnum < 0) {
9886 warn_err:
9887                 claws_unlink(tmp);
9888                 g_free(tmp);
9889                 if (action != COMPOSE_AUTO_SAVE) {
9890                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9891                                 alertpanel_error(_("Could not save draft."));
9892                         else {
9893                                 AlertValue val;
9894                                 gtkut_window_popup(compose->window);
9895                                 val = alertpanel_full(_("Could not save draft"),
9896                                         _("Could not save draft.\n"
9897                                         "Do you want to cancel exit or discard this email?"),
9898                                           _("_Cancel exit"), _("_Discard email"), NULL,
9899                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9900                                 if (val == G_ALERTALTERNATE) {
9901                                         lock = FALSE;
9902                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9903                                         compose_close(compose);
9904                                         return TRUE;
9905                                 } else {
9906                                         lock = FALSE;
9907                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9908                                         return FALSE;
9909                                 }
9910                         }
9911                 }
9912                 goto unlock;
9913         }
9914         g_free(tmp);
9915
9916         if (compose->mode == COMPOSE_REEDIT) {
9917                 compose_remove_reedit_target(compose, TRUE);
9918         }
9919
9920         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9921
9922         if (newmsginfo) {
9923                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9924                 if (target_locked)
9925                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
9926                 else
9927                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
9928                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9929                         procmsg_msginfo_set_flags(newmsginfo, 0,
9930                                                   MSG_HAS_ATTACHMENT);
9931
9932                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9933                         compose_register_draft(newmsginfo);
9934                 }
9935                 procmsg_msginfo_free(newmsginfo);
9936         }
9937         
9938         folder_item_scan(draft);
9939         
9940         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9941                 lock = FALSE;
9942                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9943                 compose_close(compose);
9944                 return TRUE;
9945         } else {
9946                 struct stat s;
9947                 gchar *path;
9948
9949                 path = folder_item_fetch_msg(draft, msgnum);
9950                 if (path == NULL) {
9951                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9952                         goto unlock;
9953                 }
9954                 if (g_stat(path, &s) < 0) {
9955                         FILE_OP_ERROR(path, "stat");
9956                         g_free(path);
9957                         goto unlock;
9958                 }
9959                 g_free(path);
9960
9961                 procmsg_msginfo_free(compose->targetinfo);
9962                 compose->targetinfo = procmsg_msginfo_new();
9963                 compose->targetinfo->msgnum = msgnum;
9964                 compose->targetinfo->size = (goffset)s.st_size;
9965                 compose->targetinfo->mtime = s.st_mtime;
9966                 compose->targetinfo->folder = draft;
9967                 if (target_locked)
9968                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9969                 compose->mode = COMPOSE_REEDIT;
9970                 
9971                 if (action == COMPOSE_AUTO_SAVE) {
9972                         compose->autosaved_draft = compose->targetinfo;
9973                 }
9974                 compose->modified = FALSE;
9975                 compose_set_title(compose);
9976         }
9977 unlock:
9978         lock = FALSE;
9979         g_mutex_unlock(compose->mutex);
9980         return TRUE;
9981 }
9982
9983 void compose_clear_exit_drafts(void)
9984 {
9985         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9986                                       DRAFTED_AT_EXIT, NULL);
9987         if (is_file_exist(filepath))
9988                 claws_unlink(filepath);
9989         
9990         g_free(filepath);
9991 }
9992
9993 void compose_reopen_exit_drafts(void)
9994 {
9995         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9996                                       DRAFTED_AT_EXIT, NULL);
9997         FILE *fp = g_fopen(filepath, "rb");
9998         gchar buf[1024];
9999         
10000         if (fp) {
10001                 while (fgets(buf, sizeof(buf), fp)) {
10002                         gchar **parts = g_strsplit(buf, "\t", 2);
10003                         const gchar *folder = parts[0];
10004                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10005                         
10006                         if (folder && *folder && msgnum > -1) {
10007                                 FolderItem *item = folder_find_item_from_identifier(folder);
10008                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10009                                 if (info)
10010                                         compose_reedit(info, FALSE);
10011                         }
10012                         g_strfreev(parts);
10013                 }       
10014                 fclose(fp);
10015         }       
10016         g_free(filepath);
10017         compose_clear_exit_drafts();
10018 }
10019
10020 static void compose_save_cb(GtkAction *action, gpointer data)
10021 {
10022         Compose *compose = (Compose *)data;
10023         compose_draft(compose, COMPOSE_KEEP_EDITING);
10024         compose->rmode = COMPOSE_REEDIT;
10025 }
10026
10027 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10028 {
10029         if (compose && file_list) {
10030                 GList *tmp;
10031
10032                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10033                         gchar *file = (gchar *) tmp->data;
10034                         gchar *utf8_filename = conv_filename_to_utf8(file);
10035                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10036                         compose_changed_cb(NULL, compose);
10037                         if (free_data) {
10038                         g_free(file);
10039                                 tmp->data = NULL;
10040                         }
10041                         g_free(utf8_filename);
10042                 }
10043         }
10044 }
10045
10046 static void compose_attach_cb(GtkAction *action, gpointer data)
10047 {
10048         Compose *compose = (Compose *)data;
10049         GList *file_list;
10050
10051         if (compose->redirect_filename != NULL)
10052                 return;
10053
10054         /* Set focus_window properly, in case we were called via popup menu,
10055          * which unsets it (via focus_out_event callback on compose window). */
10056         manage_window_focus_in(compose->window, NULL, NULL);
10057
10058         file_list = filesel_select_multiple_files_open(_("Select file"));
10059
10060         if (file_list) {
10061                 compose_attach_from_list(compose, file_list, TRUE);
10062                 g_list_free(file_list);
10063         }
10064 }
10065
10066 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10067 {
10068         Compose *compose = (Compose *)data;
10069         GList *file_list;
10070         gint files_inserted = 0;
10071
10072         file_list = filesel_select_multiple_files_open(_("Select file"));
10073
10074         if (file_list) {
10075                 GList *tmp;
10076
10077                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10078                         gchar *file = (gchar *) tmp->data;
10079                         gchar *filedup = g_strdup(file);
10080                         gchar *shortfile = g_path_get_basename(filedup);
10081                         ComposeInsertResult res;
10082                         /* insert the file if the file is short or if the user confirmed that
10083                            he/she wants to insert the large file */
10084                         res = compose_insert_file(compose, file);
10085                         if (res == COMPOSE_INSERT_READ_ERROR) {
10086                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10087                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10088                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10089                                                         "for the current encoding, insertion may be incorrect."),
10090                                                         shortfile);
10091                         } else if (res == COMPOSE_INSERT_SUCCESS)
10092                                 files_inserted++;
10093
10094                         g_free(shortfile);
10095                         g_free(filedup);
10096                         g_free(file);
10097                 }
10098                 g_list_free(file_list);
10099         }
10100
10101 #ifdef USE_ENCHANT      
10102         if (files_inserted > 0 && compose->gtkaspell && 
10103             compose->gtkaspell->check_while_typing)
10104                 gtkaspell_highlight_all(compose->gtkaspell);
10105 #endif
10106 }
10107
10108 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10109 {
10110         Compose *compose = (Compose *)data;
10111
10112         compose_insert_sig(compose, FALSE);
10113 }
10114
10115 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10116 {
10117         Compose *compose = (Compose *)data;
10118
10119         compose_insert_sig(compose, TRUE);
10120 }
10121
10122 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10123                               gpointer data)
10124 {
10125         gint x, y;
10126         Compose *compose = (Compose *)data;
10127
10128         gtkut_widget_get_uposition(widget, &x, &y);
10129         if (!compose->batch) {
10130                 prefs_common.compose_x = x;
10131                 prefs_common.compose_y = y;
10132         }
10133         if (compose->sending || compose->updating)
10134                 return TRUE;
10135         compose_close_cb(NULL, compose);
10136         return TRUE;
10137 }
10138
10139 void compose_close_toolbar(Compose *compose)
10140 {
10141         compose_close_cb(NULL, compose);
10142 }
10143
10144 static gboolean compose_can_autosave(Compose *compose)
10145 {
10146         if (compose->privacy_system && compose->use_encryption)
10147                 return prefs_common.autosave && prefs_common.autosave_encrypted;
10148         else
10149                 return prefs_common.autosave;
10150 }
10151
10152 static void compose_close_cb(GtkAction *action, gpointer data)
10153 {
10154         Compose *compose = (Compose *)data;
10155         AlertValue val;
10156
10157 #ifdef G_OS_UNIX
10158         if (compose->exteditor_tag != -1) {
10159                 if (!compose_ext_editor_kill(compose))
10160                         return;
10161         }
10162 #endif
10163
10164         if (compose->modified) {
10165                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10166                 if (!g_mutex_trylock(compose->mutex)) {
10167                         /* we don't want to lock the mutex once it's available,
10168                          * because as the only other part of compose.c locking
10169                          * it is compose_close - which means once unlocked,
10170                          * the compose struct will be freed */
10171                         debug_print("couldn't lock mutex, probably sending\n");
10172                         return;
10173                 }
10174                 if (!reedit) {
10175                         val = alertpanel(_("Discard message"),
10176                                  _("This message has been modified. Discard it?"),
10177                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10178                 } else {
10179                         val = alertpanel(_("Save changes"),
10180                                  _("This message has been modified. Save the latest changes?"),
10181                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10182                 }
10183                 g_mutex_unlock(compose->mutex);
10184                 switch (val) {
10185                 case G_ALERTDEFAULT:
10186                         if (compose_can_autosave(compose) && !reedit)
10187                                 compose_remove_draft(compose);
10188                         break;
10189                 case G_ALERTALTERNATE:
10190                         compose_draft(data, COMPOSE_QUIT_EDITING);
10191                         return;
10192                 default:
10193                         return;
10194                 }
10195         }
10196
10197         compose_close(compose);
10198 }
10199
10200 static void compose_print_cb(GtkAction *action, gpointer data)
10201 {
10202         Compose *compose = (Compose *) data;
10203
10204         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10205         if (compose->targetinfo)
10206                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10207 }
10208
10209 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10210 {
10211         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10212         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10213         Compose *compose = (Compose *) data;
10214
10215         if (active)
10216                 compose->out_encoding = (CharSet)value;
10217 }
10218
10219 static void compose_address_cb(GtkAction *action, gpointer data)
10220 {
10221         Compose *compose = (Compose *)data;
10222
10223 #ifndef USE_NEW_ADDRBOOK
10224         addressbook_open(compose);
10225 #else
10226         GError* error = NULL;
10227         addressbook_connect_signals(compose);
10228         addressbook_dbus_open(TRUE, &error);
10229         if (error) {
10230                 g_warning("%s", error->message);
10231                 g_error_free(error);
10232         }
10233 #endif
10234 }
10235
10236 static void about_show_cb(GtkAction *action, gpointer data)
10237 {
10238         about_show();
10239 }
10240
10241 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10242 {
10243         Compose *compose = (Compose *)data;
10244         Template *tmpl;
10245         gchar *msg;
10246         AlertValue val;
10247
10248         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10249         cm_return_if_fail(tmpl != NULL);
10250
10251         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10252                               tmpl->name);
10253         val = alertpanel(_("Apply template"), msg,
10254                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10255         g_free(msg);
10256
10257         if (val == G_ALERTDEFAULT)
10258                 compose_template_apply(compose, tmpl, TRUE);
10259         else if (val == G_ALERTALTERNATE)
10260                 compose_template_apply(compose, tmpl, FALSE);
10261 }
10262
10263 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10264 {
10265         Compose *compose = (Compose *)data;
10266
10267         compose_exec_ext_editor(compose);
10268 }
10269
10270 static void compose_undo_cb(GtkAction *action, gpointer data)
10271 {
10272         Compose *compose = (Compose *)data;
10273         gboolean prev_autowrap = compose->autowrap;
10274
10275         compose->autowrap = FALSE;
10276         undo_undo(compose->undostruct);
10277         compose->autowrap = prev_autowrap;
10278 }
10279
10280 static void compose_redo_cb(GtkAction *action, gpointer data)
10281 {
10282         Compose *compose = (Compose *)data;
10283         gboolean prev_autowrap = compose->autowrap;
10284         
10285         compose->autowrap = FALSE;
10286         undo_redo(compose->undostruct);
10287         compose->autowrap = prev_autowrap;
10288 }
10289
10290 static void entry_cut_clipboard(GtkWidget *entry)
10291 {
10292         if (GTK_IS_EDITABLE(entry))
10293                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10294         else if (GTK_IS_TEXT_VIEW(entry))
10295                 gtk_text_buffer_cut_clipboard(
10296                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10297                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10298                         TRUE);
10299 }
10300
10301 static void entry_copy_clipboard(GtkWidget *entry)
10302 {
10303         if (GTK_IS_EDITABLE(entry))
10304                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10305         else if (GTK_IS_TEXT_VIEW(entry))
10306                 gtk_text_buffer_copy_clipboard(
10307                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10308                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10309 }
10310
10311 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10312                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10313 {
10314         if (GTK_IS_TEXT_VIEW(entry)) {
10315                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10316                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10317                 GtkTextIter start_iter, end_iter;
10318                 gint start, end;
10319                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10320
10321                 if (contents == NULL)
10322                         return;
10323         
10324                 /* we shouldn't delete the selection when middle-click-pasting, or we
10325                  * can't mid-click-paste our own selection */
10326                 if (clip != GDK_SELECTION_PRIMARY) {
10327                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10328                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10329                 }
10330                 
10331                 if (insert_place == NULL) {
10332                         /* if insert_place isn't specified, insert at the cursor.
10333                          * used for Ctrl-V pasting */
10334                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10335                         start = gtk_text_iter_get_offset(&start_iter);
10336                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10337                 } else {
10338                         /* if insert_place is specified, paste here.
10339                          * used for mid-click-pasting */
10340                         start = gtk_text_iter_get_offset(insert_place);
10341                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10342                         if (prefs_common.primary_paste_unselects)
10343                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10344                 }
10345                 
10346                 if (!wrap) {
10347                         /* paste unwrapped: mark the paste so it's not wrapped later */
10348                         end = start + strlen(contents);
10349                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10350                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10351                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10352                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10353                         /* rewrap paragraph now (after a mid-click-paste) */
10354                         mark_start = gtk_text_buffer_get_insert(buffer);
10355                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10356                         gtk_text_iter_backward_char(&start_iter);
10357                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10358                 }
10359         } else if (GTK_IS_EDITABLE(entry))
10360                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10361
10362         compose->modified = TRUE;
10363 }
10364
10365 static void entry_allsel(GtkWidget *entry)
10366 {
10367         if (GTK_IS_EDITABLE(entry))
10368                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10369         else if (GTK_IS_TEXT_VIEW(entry)) {
10370                 GtkTextIter startiter, enditer;
10371                 GtkTextBuffer *textbuf;
10372
10373                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10374                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10375                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10376
10377                 gtk_text_buffer_move_mark_by_name(textbuf, 
10378                         "selection_bound", &startiter);
10379                 gtk_text_buffer_move_mark_by_name(textbuf, 
10380                         "insert", &enditer);
10381         }
10382 }
10383
10384 static void compose_cut_cb(GtkAction *action, gpointer data)
10385 {
10386         Compose *compose = (Compose *)data;
10387         if (compose->focused_editable 
10388 #ifndef GENERIC_UMPC
10389             && gtk_widget_has_focus(compose->focused_editable)
10390 #endif
10391             )
10392                 entry_cut_clipboard(compose->focused_editable);
10393 }
10394
10395 static void compose_copy_cb(GtkAction *action, gpointer data)
10396 {
10397         Compose *compose = (Compose *)data;
10398         if (compose->focused_editable 
10399 #ifndef GENERIC_UMPC
10400             && gtk_widget_has_focus(compose->focused_editable)
10401 #endif
10402             )
10403                 entry_copy_clipboard(compose->focused_editable);
10404 }
10405
10406 static void compose_paste_cb(GtkAction *action, gpointer data)
10407 {
10408         Compose *compose = (Compose *)data;
10409         gint prev_autowrap;
10410         GtkTextBuffer *buffer;
10411         BLOCK_WRAP();
10412         if (compose->focused_editable &&
10413 #ifndef GENERIC_UMPC
10414             gtk_widget_has_focus(compose->focused_editable)
10415 #endif
10416                 )
10417                 entry_paste_clipboard(compose, compose->focused_editable, 
10418                                 prefs_common.linewrap_pastes,
10419                                 GDK_SELECTION_CLIPBOARD, NULL);
10420         UNBLOCK_WRAP();
10421
10422 #ifdef USE_ENCHANT
10423         if (
10424 #ifndef GENERIC_UMPC
10425                 gtk_widget_has_focus(compose->text) &&
10426 #endif
10427             compose->gtkaspell && 
10428             compose->gtkaspell->check_while_typing)
10429                 gtkaspell_highlight_all(compose->gtkaspell);
10430 #endif
10431 }
10432
10433 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10434 {
10435         Compose *compose = (Compose *)data;
10436         gint wrap_quote = prefs_common.linewrap_quote;
10437         if (compose->focused_editable 
10438 #ifndef GENERIC_UMPC
10439             && gtk_widget_has_focus(compose->focused_editable)
10440 #endif
10441             ) {
10442                 /* let text_insert() (called directly or at a later time
10443                  * after the gtk_editable_paste_clipboard) know that 
10444                  * text is to be inserted as a quotation. implemented
10445                  * by using a simple refcount... */
10446                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10447                                                 G_OBJECT(compose->focused_editable),
10448                                                 "paste_as_quotation"));
10449                 g_object_set_data(G_OBJECT(compose->focused_editable),
10450                                     "paste_as_quotation",
10451                                     GINT_TO_POINTER(paste_as_quotation + 1));
10452                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10453                 entry_paste_clipboard(compose, compose->focused_editable, 
10454                                 prefs_common.linewrap_pastes,
10455                                 GDK_SELECTION_CLIPBOARD, NULL);
10456                 prefs_common.linewrap_quote = wrap_quote;
10457         }
10458 }
10459
10460 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10461 {
10462         Compose *compose = (Compose *)data;
10463         gint prev_autowrap;
10464         GtkTextBuffer *buffer;
10465         BLOCK_WRAP();
10466         if (compose->focused_editable 
10467 #ifndef GENERIC_UMPC
10468             && gtk_widget_has_focus(compose->focused_editable)
10469 #endif
10470             )
10471                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10472                         GDK_SELECTION_CLIPBOARD, NULL);
10473         UNBLOCK_WRAP();
10474
10475 #ifdef USE_ENCHANT
10476         if (
10477 #ifndef GENERIC_UMPC
10478                 gtk_widget_has_focus(compose->text) &&
10479 #endif
10480             compose->gtkaspell && 
10481             compose->gtkaspell->check_while_typing)
10482                 gtkaspell_highlight_all(compose->gtkaspell);
10483 #endif
10484 }
10485
10486 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10487 {
10488         Compose *compose = (Compose *)data;
10489         gint prev_autowrap;
10490         GtkTextBuffer *buffer;
10491         BLOCK_WRAP();
10492         if (compose->focused_editable 
10493 #ifndef GENERIC_UMPC
10494             && gtk_widget_has_focus(compose->focused_editable)
10495 #endif
10496             )
10497                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10498                         GDK_SELECTION_CLIPBOARD, NULL);
10499         UNBLOCK_WRAP();
10500
10501 #ifdef USE_ENCHANT
10502         if (
10503 #ifndef GENERIC_UMPC
10504                 gtk_widget_has_focus(compose->text) &&
10505 #endif
10506             compose->gtkaspell &&
10507             compose->gtkaspell->check_while_typing)
10508                 gtkaspell_highlight_all(compose->gtkaspell);
10509 #endif
10510 }
10511
10512 static void compose_allsel_cb(GtkAction *action, gpointer data)
10513 {
10514         Compose *compose = (Compose *)data;
10515         if (compose->focused_editable 
10516 #ifndef GENERIC_UMPC
10517             && gtk_widget_has_focus(compose->focused_editable)
10518 #endif
10519             )
10520                 entry_allsel(compose->focused_editable);
10521 }
10522
10523 static void textview_move_beginning_of_line (GtkTextView *text)
10524 {
10525         GtkTextBuffer *buffer;
10526         GtkTextMark *mark;
10527         GtkTextIter ins;
10528
10529         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10530
10531         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10532         mark = gtk_text_buffer_get_insert(buffer);
10533         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10534         gtk_text_iter_set_line_offset(&ins, 0);
10535         gtk_text_buffer_place_cursor(buffer, &ins);
10536 }
10537
10538 static void textview_move_forward_character (GtkTextView *text)
10539 {
10540         GtkTextBuffer *buffer;
10541         GtkTextMark *mark;
10542         GtkTextIter ins;
10543
10544         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10545
10546         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10547         mark = gtk_text_buffer_get_insert(buffer);
10548         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10549         if (gtk_text_iter_forward_cursor_position(&ins))
10550                 gtk_text_buffer_place_cursor(buffer, &ins);
10551 }
10552
10553 static void textview_move_backward_character (GtkTextView *text)
10554 {
10555         GtkTextBuffer *buffer;
10556         GtkTextMark *mark;
10557         GtkTextIter ins;
10558
10559         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10560
10561         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10562         mark = gtk_text_buffer_get_insert(buffer);
10563         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10564         if (gtk_text_iter_backward_cursor_position(&ins))
10565                 gtk_text_buffer_place_cursor(buffer, &ins);
10566 }
10567
10568 static void textview_move_forward_word (GtkTextView *text)
10569 {
10570         GtkTextBuffer *buffer;
10571         GtkTextMark *mark;
10572         GtkTextIter ins;
10573         gint count;
10574
10575         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10576
10577         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10578         mark = gtk_text_buffer_get_insert(buffer);
10579         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10580         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10581         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10582                 gtk_text_iter_backward_word_start(&ins);
10583                 gtk_text_buffer_place_cursor(buffer, &ins);
10584         }
10585 }
10586
10587 static void textview_move_backward_word (GtkTextView *text)
10588 {
10589         GtkTextBuffer *buffer;
10590         GtkTextMark *mark;
10591         GtkTextIter ins;
10592
10593         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10594
10595         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10596         mark = gtk_text_buffer_get_insert(buffer);
10597         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10598         if (gtk_text_iter_backward_word_starts(&ins, 1))
10599                 gtk_text_buffer_place_cursor(buffer, &ins);
10600 }
10601
10602 static void textview_move_end_of_line (GtkTextView *text)
10603 {
10604         GtkTextBuffer *buffer;
10605         GtkTextMark *mark;
10606         GtkTextIter ins;
10607
10608         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10609
10610         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10611         mark = gtk_text_buffer_get_insert(buffer);
10612         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10613         if (gtk_text_iter_forward_to_line_end(&ins))
10614                 gtk_text_buffer_place_cursor(buffer, &ins);
10615 }
10616
10617 static void textview_move_next_line (GtkTextView *text)
10618 {
10619         GtkTextBuffer *buffer;
10620         GtkTextMark *mark;
10621         GtkTextIter ins;
10622         gint offset;
10623
10624         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10625
10626         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10627         mark = gtk_text_buffer_get_insert(buffer);
10628         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10629         offset = gtk_text_iter_get_line_offset(&ins);
10630         if (gtk_text_iter_forward_line(&ins)) {
10631                 gtk_text_iter_set_line_offset(&ins, offset);
10632                 gtk_text_buffer_place_cursor(buffer, &ins);
10633         }
10634 }
10635
10636 static void textview_move_previous_line (GtkTextView *text)
10637 {
10638         GtkTextBuffer *buffer;
10639         GtkTextMark *mark;
10640         GtkTextIter ins;
10641         gint offset;
10642
10643         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10644
10645         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10646         mark = gtk_text_buffer_get_insert(buffer);
10647         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10648         offset = gtk_text_iter_get_line_offset(&ins);
10649         if (gtk_text_iter_backward_line(&ins)) {
10650                 gtk_text_iter_set_line_offset(&ins, offset);
10651                 gtk_text_buffer_place_cursor(buffer, &ins);
10652         }
10653 }
10654
10655 static void textview_delete_forward_character (GtkTextView *text)
10656 {
10657         GtkTextBuffer *buffer;
10658         GtkTextMark *mark;
10659         GtkTextIter ins, end_iter;
10660
10661         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10662
10663         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10664         mark = gtk_text_buffer_get_insert(buffer);
10665         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10666         end_iter = ins;
10667         if (gtk_text_iter_forward_char(&end_iter)) {
10668                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10669         }
10670 }
10671
10672 static void textview_delete_backward_character (GtkTextView *text)
10673 {
10674         GtkTextBuffer *buffer;
10675         GtkTextMark *mark;
10676         GtkTextIter ins, end_iter;
10677
10678         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10679
10680         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10681         mark = gtk_text_buffer_get_insert(buffer);
10682         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10683         end_iter = ins;
10684         if (gtk_text_iter_backward_char(&end_iter)) {
10685                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10686         }
10687 }
10688
10689 static void textview_delete_forward_word (GtkTextView *text)
10690 {
10691         GtkTextBuffer *buffer;
10692         GtkTextMark *mark;
10693         GtkTextIter ins, end_iter;
10694
10695         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10696
10697         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10698         mark = gtk_text_buffer_get_insert(buffer);
10699         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10700         end_iter = ins;
10701         if (gtk_text_iter_forward_word_end(&end_iter)) {
10702                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10703         }
10704 }
10705
10706 static void textview_delete_backward_word (GtkTextView *text)
10707 {
10708         GtkTextBuffer *buffer;
10709         GtkTextMark *mark;
10710         GtkTextIter ins, end_iter;
10711
10712         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10713
10714         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10715         mark = gtk_text_buffer_get_insert(buffer);
10716         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10717         end_iter = ins;
10718         if (gtk_text_iter_backward_word_start(&end_iter)) {
10719                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10720         }
10721 }
10722
10723 static void textview_delete_line (GtkTextView *text)
10724 {
10725         GtkTextBuffer *buffer;
10726         GtkTextMark *mark;
10727         GtkTextIter ins, start_iter, end_iter;
10728
10729         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10730
10731         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10732         mark = gtk_text_buffer_get_insert(buffer);
10733         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10734
10735         start_iter = ins;
10736         gtk_text_iter_set_line_offset(&start_iter, 0);
10737
10738         end_iter = ins;
10739         if (gtk_text_iter_ends_line(&end_iter)){
10740                 if (!gtk_text_iter_forward_char(&end_iter))
10741                         gtk_text_iter_backward_char(&start_iter);
10742         }
10743         else 
10744                 gtk_text_iter_forward_to_line_end(&end_iter);
10745         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10746 }
10747
10748 static void textview_delete_to_line_end (GtkTextView *text)
10749 {
10750         GtkTextBuffer *buffer;
10751         GtkTextMark *mark;
10752         GtkTextIter ins, end_iter;
10753
10754         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10755
10756         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10757         mark = gtk_text_buffer_get_insert(buffer);
10758         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10759         end_iter = ins;
10760         if (gtk_text_iter_ends_line(&end_iter))
10761                 gtk_text_iter_forward_char(&end_iter);
10762         else
10763                 gtk_text_iter_forward_to_line_end(&end_iter);
10764         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10765 }
10766
10767 #define DO_ACTION(name, act) {                                          \
10768         if(!strcmp(name, a_name)) {                                     \
10769                 return act;                                             \
10770         }                                                               \
10771 }
10772 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10773 {
10774         const gchar *a_name = gtk_action_get_name(action);
10775         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10776         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10777         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10778         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10779         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10780         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10781         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10782         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10783         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10784         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10785         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10786         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10787         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10788         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10789         return -1;
10790 }
10791
10792 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10793 {
10794         Compose *compose = (Compose *)data;
10795         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10796         ComposeCallAdvancedAction action = -1;
10797         
10798         action = compose_call_advanced_action_from_path(gaction);
10799
10800         static struct {
10801                 void (*do_action) (GtkTextView *text);
10802         } action_table[] = {
10803                 {textview_move_beginning_of_line},
10804                 {textview_move_forward_character},
10805                 {textview_move_backward_character},
10806                 {textview_move_forward_word},
10807                 {textview_move_backward_word},
10808                 {textview_move_end_of_line},
10809                 {textview_move_next_line},
10810                 {textview_move_previous_line},
10811                 {textview_delete_forward_character},
10812                 {textview_delete_backward_character},
10813                 {textview_delete_forward_word},
10814                 {textview_delete_backward_word},
10815                 {textview_delete_line},
10816                 {textview_delete_to_line_end}
10817         };
10818
10819         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10820
10821         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10822             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10823                 if (action_table[action].do_action)
10824                         action_table[action].do_action(text);
10825                 else
10826                         g_warning("Not implemented yet.");
10827         }
10828 }
10829
10830 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10831 {
10832         GtkAllocation allocation;
10833         GtkWidget *parent;
10834         gchar *str = NULL;
10835         
10836         if (GTK_IS_EDITABLE(widget)) {
10837                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10838                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10839                         strlen(str));
10840                 g_free(str);
10841                 if ((parent = gtk_widget_get_parent(widget))
10842                  && (parent = gtk_widget_get_parent(parent))
10843                  && (parent = gtk_widget_get_parent(parent))) {
10844                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
10845                                 gtk_widget_get_allocation(widget, &allocation);
10846                                 gint y = allocation.y;
10847                                 gint height = allocation.height;
10848                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10849                                         (GTK_SCROLLED_WINDOW(parent));
10850
10851                                 gfloat value = gtk_adjustment_get_value(shown);
10852                                 gfloat upper = gtk_adjustment_get_upper(shown);
10853                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
10854                                 if (y < (int)value) {
10855                                         gtk_adjustment_set_value(shown, y - 1);
10856                                 }
10857                                 if ((y + height) > ((int)value + (int)page_size)) {
10858                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
10859                                                 gtk_adjustment_set_value(shown, 
10860                                                         y + height - (int)page_size - 1);
10861                                         } else {
10862                                                 gtk_adjustment_set_value(shown, 
10863                                                         (int)upper - (int)page_size - 1);
10864                                         }
10865                                 }
10866                         }
10867                 }
10868         }
10869
10870         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10871                 compose->focused_editable = widget;
10872         
10873 #ifdef GENERIC_UMPC
10874         if (GTK_IS_TEXT_VIEW(widget) 
10875             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10876                 g_object_ref(compose->notebook);
10877                 g_object_ref(compose->edit_vbox);
10878                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10879                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10880                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10881                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10882                 g_object_unref(compose->notebook);
10883                 g_object_unref(compose->edit_vbox);
10884                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10885                                         G_CALLBACK(compose_grab_focus_cb),
10886                                         compose);
10887                 gtk_widget_grab_focus(widget);
10888                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10889                                         G_CALLBACK(compose_grab_focus_cb),
10890                                         compose);
10891         } else if (!GTK_IS_TEXT_VIEW(widget) 
10892                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10893                 g_object_ref(compose->notebook);
10894                 g_object_ref(compose->edit_vbox);
10895                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10896                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10897                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10898                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10899                 g_object_unref(compose->notebook);
10900                 g_object_unref(compose->edit_vbox);
10901                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10902                                         G_CALLBACK(compose_grab_focus_cb),
10903                                         compose);
10904                 gtk_widget_grab_focus(widget);
10905                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10906                                         G_CALLBACK(compose_grab_focus_cb),
10907                                         compose);
10908         }
10909 #endif
10910 }
10911
10912 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10913 {
10914         compose->modified = TRUE;
10915 //      compose_beautify_paragraph(compose, NULL, TRUE);
10916 #ifndef GENERIC_UMPC
10917         compose_set_title(compose);
10918 #endif
10919 }
10920
10921 static void compose_wrap_cb(GtkAction *action, gpointer data)
10922 {
10923         Compose *compose = (Compose *)data;
10924         compose_beautify_paragraph(compose, NULL, TRUE);
10925 }
10926
10927 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10928 {
10929         Compose *compose = (Compose *)data;
10930         compose_wrap_all_full(compose, TRUE);
10931 }
10932
10933 static void compose_find_cb(GtkAction *action, gpointer data)
10934 {
10935         Compose *compose = (Compose *)data;
10936
10937         message_search_compose(compose);
10938 }
10939
10940 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10941                                          gpointer        data)
10942 {
10943         Compose *compose = (Compose *)data;
10944         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10945         if (compose->autowrap)
10946                 compose_wrap_all_full(compose, TRUE);
10947         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10948 }
10949
10950 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10951                                          gpointer        data)
10952 {
10953         Compose *compose = (Compose *)data;
10954         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10955 }
10956
10957 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10958 {
10959         Compose *compose = (Compose *)data;
10960
10961         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10962 }
10963
10964 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10965 {
10966         Compose *compose = (Compose *)data;
10967
10968         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10969 }
10970
10971 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10972 {
10973         g_free(compose->privacy_system);
10974
10975         compose->privacy_system = g_strdup(account->default_privacy_system);
10976         compose_update_privacy_system_menu_item(compose, warn);
10977 }
10978
10979 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10980 {
10981         Compose *compose = (Compose *)data;
10982
10983         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10984                 gtk_widget_show(compose->ruler_hbox);
10985                 prefs_common.show_ruler = TRUE;
10986         } else {
10987                 gtk_widget_hide(compose->ruler_hbox);
10988                 gtk_widget_queue_resize(compose->edit_vbox);
10989                 prefs_common.show_ruler = FALSE;
10990         }
10991 }
10992
10993 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10994                                              GdkDragContext     *context,
10995                                              gint                x,
10996                                              gint                y,
10997                                              GtkSelectionData   *data,
10998                                              guint               info,
10999                                              guint               time,
11000                                              gpointer            user_data)
11001 {
11002         Compose *compose = (Compose *)user_data;
11003         GList *list, *tmp;
11004         GdkAtom type;
11005
11006         type = gtk_selection_data_get_data_type(data);
11007         if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11008 #ifdef G_OS_WIN32
11009          || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
11010 #endif
11011            ) && gtk_drag_get_source_widget(context) != 
11012                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11013                 list = uri_list_extract_filenames(
11014                         (const gchar *)gtk_selection_data_get_data(data));
11015                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11016                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11017                         compose_attach_append
11018                                 (compose, (const gchar *)tmp->data,
11019                                  utf8_filename, NULL, NULL);
11020                         g_free(utf8_filename);
11021                 }
11022                 if (list) compose_changed_cb(NULL, compose);
11023                 list_free_strings(list);
11024                 g_list_free(list);
11025         } else if (gtk_drag_get_source_widget(context) 
11026                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11027                 /* comes from our summaryview */
11028                 SummaryView * summaryview = NULL;
11029                 GSList * list = NULL, *cur = NULL;
11030                 
11031                 if (mainwindow_get_mainwindow())
11032                         summaryview = mainwindow_get_mainwindow()->summaryview;
11033                 
11034                 if (summaryview)
11035                         list = summary_get_selected_msg_list(summaryview);
11036                 
11037                 for (cur = list; cur; cur = cur->next) {
11038                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11039                         gchar *file = NULL;
11040                         if (msginfo)
11041                                 file = procmsg_get_message_file_full(msginfo, 
11042                                         TRUE, TRUE);
11043                         if (file) {
11044                                 compose_attach_append(compose, (const gchar *)file, 
11045                                         (const gchar *)file, "message/rfc822", NULL);
11046                                 g_free(file);
11047                         }
11048                 }
11049                 g_slist_free(list);
11050         }
11051 }
11052
11053 static gboolean compose_drag_drop(GtkWidget *widget,
11054                                   GdkDragContext *drag_context,
11055                                   gint x, gint y,
11056                                   guint time, gpointer user_data)
11057 {
11058         /* not handling this signal makes compose_insert_drag_received_cb
11059          * called twice */
11060         return TRUE;                                     
11061 }
11062
11063 static gboolean completion_set_focus_to_subject
11064                                         (GtkWidget    *widget,
11065                                          GdkEventKey  *event,
11066                                          Compose      *compose)
11067 {
11068         GtkTextBuffer *buffer;
11069         GtkTextMark *mark;
11070         GtkTextIter iter;
11071
11072         cm_return_val_if_fail(compose != NULL, FALSE);
11073
11074         /* make backtab move to subject field */
11075         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11076                 gtk_widget_grab_focus(compose->subject_entry);
11077                 return TRUE;
11078         }
11079
11080         // Up key should also move the focus to subject field, if the cursor
11081         // is on the first line.
11082         if ((event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up)
11083           && (event->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == 0) {
11084                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
11085                 g_return_val_if_fail(buffer != NULL, FALSE);
11086
11087                 mark = gtk_text_buffer_get_mark(buffer, "insert");
11088                 g_return_val_if_fail(mark != NULL, FALSE);
11089
11090                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
11091
11092                 if (gtk_text_iter_get_line(&iter) == 0) {
11093                         gtk_widget_grab_focus(compose->subject_entry);
11094                         return TRUE;
11095                 }
11096         }
11097
11098         return FALSE;
11099 }
11100
11101 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11102                                              GdkDragContext     *drag_context,
11103                                              gint                x,
11104                                              gint                y,
11105                                              GtkSelectionData   *data,
11106                                              guint               info,
11107                                              guint               time,
11108                                              gpointer            user_data)
11109 {
11110         Compose *compose = (Compose *)user_data;
11111         GList *list, *tmp;
11112         GdkAtom type;
11113
11114         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11115          * does not work */
11116         type = gtk_selection_data_get_data_type(data);
11117 #ifndef G_OS_WIN32
11118         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11119 #else
11120         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
11121 #endif
11122                 AlertValue val = G_ALERTDEFAULT;
11123                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11124
11125                 list = uri_list_extract_filenames(ddata);
11126                 if (list == NULL && strstr(ddata, "://")) {
11127                         /* Assume a list of no files, and data has ://, is a remote link */
11128                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11129                         gchar *tmpfile = get_tmp_file();
11130                         str_write_to_file(tmpdata, tmpfile);
11131                         g_free(tmpdata);  
11132                         compose_insert_file(compose, tmpfile);
11133                         claws_unlink(tmpfile);
11134                         g_free(tmpfile);
11135                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11136                         compose_beautify_paragraph(compose, NULL, TRUE);
11137                         return;
11138                 }
11139                 switch (prefs_common.compose_dnd_mode) {
11140                         case COMPOSE_DND_ASK:
11141                                 val = alertpanel_full(_("Insert or attach?"),
11142                                          _("Do you want to insert the contents of the file(s) "
11143                                            "into the message body, or attach it to the email?"),
11144                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
11145                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11146                                 break;
11147                         case COMPOSE_DND_INSERT:
11148                                 val = G_ALERTALTERNATE;
11149                                 break;
11150                         case COMPOSE_DND_ATTACH:
11151                                 val = G_ALERTOTHER;
11152                                 break;
11153                         default:
11154                                 /* unexpected case */
11155                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11156                 }
11157
11158                 if (val & G_ALERTDISABLE) {
11159                         val &= ~G_ALERTDISABLE;
11160                         /* remember what action to perform by default, only if we don't click Cancel */
11161                         if (val == G_ALERTALTERNATE)
11162                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11163                         else if (val == G_ALERTOTHER)
11164                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11165                 }
11166
11167                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11168                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11169                         list_free_strings(list);
11170                         g_list_free(list);
11171                         return;
11172                 } else if (val == G_ALERTOTHER) {
11173                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11174                         list_free_strings(list);
11175                         g_list_free(list);
11176                         return;
11177                 } 
11178
11179                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11180                         compose_insert_file(compose, (const gchar *)tmp->data);
11181                 }
11182                 list_free_strings(list);
11183                 g_list_free(list);
11184                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11185                 return;
11186         }
11187 }
11188
11189 static void compose_header_drag_received_cb (GtkWidget          *widget,
11190                                              GdkDragContext     *drag_context,
11191                                              gint                x,
11192                                              gint                y,
11193                                              GtkSelectionData   *data,
11194                                              guint               info,
11195                                              guint               time,
11196                                              gpointer            user_data)
11197 {
11198         GtkEditable *entry = (GtkEditable *)user_data;
11199         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11200
11201         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11202          * does not work */
11203
11204         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11205                 gchar *decoded=g_new(gchar, strlen(email));
11206                 int start = 0;
11207
11208                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11209                 gtk_editable_delete_text(entry, 0, -1);
11210                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11211                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11212                 g_free(decoded);
11213                 return;
11214         }
11215         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11216 }
11217
11218 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11219 {
11220         Compose *compose = (Compose *)data;
11221
11222         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11223                 compose->return_receipt = TRUE;
11224         else
11225                 compose->return_receipt = FALSE;
11226 }
11227
11228 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11229 {
11230         Compose *compose = (Compose *)data;
11231
11232         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11233                 compose->remove_references = TRUE;
11234         else
11235                 compose->remove_references = FALSE;
11236 }
11237
11238 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11239                                         ComposeHeaderEntry *headerentry)
11240 {
11241         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11242         return FALSE;
11243 }
11244
11245 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11246                                             GdkEventKey *event,
11247                                             ComposeHeaderEntry *headerentry)
11248 {
11249         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11250             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11251             !(event->state & GDK_MODIFIER_MASK) &&
11252             (event->keyval == GDK_KEY_BackSpace) &&
11253             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11254                 gtk_container_remove
11255                         (GTK_CONTAINER(headerentry->compose->header_table),
11256                          headerentry->combo);
11257                 gtk_container_remove
11258                         (GTK_CONTAINER(headerentry->compose->header_table),
11259                          headerentry->entry);
11260                 headerentry->compose->header_list =
11261                         g_slist_remove(headerentry->compose->header_list,
11262                                        headerentry);
11263                 g_free(headerentry);
11264         } else  if (event->keyval == GDK_KEY_Tab) {
11265                 if (headerentry->compose->header_last == headerentry) {
11266                         /* Override default next focus, and give it to subject_entry
11267                          * instead of notebook tabs
11268                          */
11269                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11270                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11271                         return TRUE;
11272                 }
11273         }
11274         return FALSE;
11275 }
11276
11277 static gboolean scroll_postpone(gpointer data)
11278 {
11279         Compose *compose = (Compose *)data;
11280
11281         if (compose->batch)
11282                 return FALSE;
11283
11284         GTK_EVENTS_FLUSH();
11285         compose_show_first_last_header(compose, FALSE);
11286         return FALSE;
11287 }
11288
11289 static void compose_headerentry_changed_cb(GtkWidget *entry,
11290                                     ComposeHeaderEntry *headerentry)
11291 {
11292         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11293                 compose_create_header_entry(headerentry->compose);
11294                 g_signal_handlers_disconnect_matched
11295                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11296                          0, 0, NULL, NULL, headerentry);
11297
11298                 if (!headerentry->compose->batch)
11299                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11300         }
11301 }
11302
11303 static gboolean compose_defer_auto_save_draft(Compose *compose)
11304 {
11305         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11306         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11307         return FALSE;
11308 }
11309
11310 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11311 {
11312         GtkAdjustment *vadj;
11313
11314         cm_return_if_fail(compose);
11315
11316         if(compose->batch)
11317                 return;
11318
11319         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11320         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11321         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11322                                 gtk_widget_get_parent(compose->header_table)));
11323         gtk_adjustment_set_value(vadj, (show_first ?
11324                                 gtk_adjustment_get_lower(vadj) :
11325                                 (gtk_adjustment_get_upper(vadj) -
11326                                 gtk_adjustment_get_page_size(vadj))));
11327         gtk_adjustment_changed(vadj);
11328 }
11329
11330 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11331                           const gchar *text, gint len, Compose *compose)
11332 {
11333         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11334                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11335         GtkTextMark *mark;
11336
11337         cm_return_if_fail(text != NULL);
11338
11339         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11340                                         G_CALLBACK(text_inserted),
11341                                         compose);
11342         if (paste_as_quotation) {
11343                 gchar *new_text;
11344                 const gchar *qmark;
11345                 guint pos = 0;
11346                 GtkTextIter start_iter;
11347
11348                 if (len < 0)
11349                         len = strlen(text);
11350
11351                 new_text = g_strndup(text, len);
11352
11353                 qmark = compose_quote_char_from_context(compose);
11354
11355                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11356                 gtk_text_buffer_place_cursor(buffer, iter);
11357
11358                 pos = gtk_text_iter_get_offset(iter);
11359
11360                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11361                                                   _("Quote format error at line %d."));
11362                 quote_fmt_reset_vartable();
11363                 g_free(new_text);
11364                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11365                                   GINT_TO_POINTER(paste_as_quotation - 1));
11366                                   
11367                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11368                 gtk_text_buffer_place_cursor(buffer, iter);
11369                 gtk_text_buffer_delete_mark(buffer, mark);
11370
11371                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11372                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11373                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11374                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11375                 gtk_text_buffer_delete_mark(buffer, mark);
11376         } else {
11377                 if (strcmp(text, "\n") || compose->automatic_break
11378                 || gtk_text_iter_starts_line(iter)) {
11379                         GtkTextIter before_ins;
11380                         gtk_text_buffer_insert(buffer, iter, text, len);
11381                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11382                                 before_ins = *iter; 
11383                                 gtk_text_iter_backward_chars(&before_ins, len);
11384                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11385                         }
11386                 } else {
11387                         /* check if the preceding is just whitespace or quote */
11388                         GtkTextIter start_line;
11389                         gchar *tmp = NULL, *quote = NULL;
11390                         gint quote_len = 0, is_normal = 0;
11391                         start_line = *iter;
11392                         gtk_text_iter_set_line_offset(&start_line, 0); 
11393                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11394                         g_strstrip(tmp);
11395
11396                         if (*tmp == '\0') {
11397                                 is_normal = 1;
11398                         } else {
11399                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11400                                 if (quote)
11401                                         is_normal = 1;
11402                                 g_free(quote);
11403                         }
11404                         g_free(tmp);
11405                         
11406                         if (is_normal) {
11407                                 gtk_text_buffer_insert(buffer, iter, text, len);
11408                         } else {
11409                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11410                                         iter, text, len, "no_join", NULL);
11411                         }
11412                 }
11413         }
11414         
11415         if (!paste_as_quotation) {
11416                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11417                 compose_beautify_paragraph(compose, iter, FALSE);
11418                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11419                 gtk_text_buffer_delete_mark(buffer, mark);
11420         }
11421
11422         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11423                                           G_CALLBACK(text_inserted),
11424                                           compose);
11425         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11426
11427         if (compose_can_autosave(compose) && 
11428             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11429             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11430                 compose->draft_timeout_tag = g_timeout_add
11431                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11432 }
11433
11434 #if USE_ENCHANT
11435 static void compose_check_all(GtkAction *action, gpointer data)
11436 {
11437         Compose *compose = (Compose *)data;
11438         if (!compose->gtkaspell)
11439                 return;
11440                 
11441         if (gtk_widget_has_focus(compose->subject_entry))
11442                 claws_spell_entry_check_all(
11443                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11444         else
11445                 gtkaspell_check_all(compose->gtkaspell);
11446 }
11447
11448 static void compose_highlight_all(GtkAction *action, gpointer data)
11449 {
11450         Compose *compose = (Compose *)data;
11451         if (compose->gtkaspell) {
11452                 claws_spell_entry_recheck_all(
11453                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11454                 gtkaspell_highlight_all(compose->gtkaspell);
11455         }
11456 }
11457
11458 static void compose_check_backwards(GtkAction *action, gpointer data)
11459 {
11460         Compose *compose = (Compose *)data;
11461         if (!compose->gtkaspell) {
11462                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11463                 return;
11464         }
11465
11466         if (gtk_widget_has_focus(compose->subject_entry))
11467                 claws_spell_entry_check_backwards(
11468                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11469         else
11470                 gtkaspell_check_backwards(compose->gtkaspell);
11471 }
11472
11473 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11474 {
11475         Compose *compose = (Compose *)data;
11476         if (!compose->gtkaspell) {
11477                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11478                 return;
11479         }
11480
11481         if (gtk_widget_has_focus(compose->subject_entry))
11482                 claws_spell_entry_check_forwards_go(
11483                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11484         else
11485                 gtkaspell_check_forwards_go(compose->gtkaspell);
11486 }
11487 #endif
11488
11489 /*!
11490  *\brief        Guess originating forward account from MsgInfo and several 
11491  *              "common preference" settings. Return NULL if no guess. 
11492  */
11493 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11494 {
11495         PrefsAccount *account = NULL;
11496         
11497         cm_return_val_if_fail(msginfo, NULL);
11498         cm_return_val_if_fail(msginfo->folder, NULL);
11499         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11500
11501         if (msginfo->folder->prefs->enable_default_account)
11502                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11503                 
11504         if (!account) 
11505                 account = msginfo->folder->folder->account;
11506                 
11507         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11508                 gchar *to;
11509                 Xstrdup_a(to, msginfo->to, return NULL);
11510                 extract_address(to);
11511                 account = account_find_from_address(to, FALSE);
11512         }
11513
11514         if (!account && prefs_common.forward_account_autosel) {
11515                 gchar cc[BUFFSIZE];
11516                 if (!procheader_get_header_from_msginfo
11517                         (msginfo, cc,sizeof cc , "Cc:")) { 
11518                         gchar *buf = cc + strlen("Cc:");
11519                         extract_address(buf);
11520                         account = account_find_from_address(buf, FALSE);
11521                 }
11522         }
11523         
11524         if (!account && prefs_common.forward_account_autosel) {
11525                 gchar deliveredto[BUFFSIZE];
11526                 if (!procheader_get_header_from_msginfo
11527                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11528                         gchar *buf = deliveredto + strlen("Delivered-To:");
11529                         extract_address(buf);
11530                         account = account_find_from_address(buf, FALSE);
11531                 }
11532         }
11533         
11534         return account;
11535 }
11536
11537 gboolean compose_close(Compose *compose)
11538 {
11539         gint x, y;
11540
11541         cm_return_val_if_fail(compose, FALSE);
11542
11543         if (!g_mutex_trylock(compose->mutex)) {
11544                 /* we have to wait for the (possibly deferred by auto-save)
11545                  * drafting to be done, before destroying the compose under
11546                  * it. */
11547                 debug_print("waiting for drafting to finish...\n");
11548                 compose_allow_user_actions(compose, FALSE);
11549                 if (compose->close_timeout_tag == 0) {
11550                         compose->close_timeout_tag = 
11551                                 g_timeout_add (500, (GSourceFunc) compose_close,
11552                                 compose);
11553                 }
11554                 return TRUE;
11555         }
11556
11557         if (compose->draft_timeout_tag >= 0) {
11558                 g_source_remove(compose->draft_timeout_tag);
11559                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11560         }
11561
11562         gtkut_widget_get_uposition(compose->window, &x, &y);
11563         if (!compose->batch) {
11564                 prefs_common.compose_x = x;
11565                 prefs_common.compose_y = y;
11566         }
11567         g_mutex_unlock(compose->mutex);
11568         compose_destroy(compose);
11569         return FALSE;
11570 }
11571
11572 /**
11573  * Add entry field for each address in list.
11574  * \param compose     E-Mail composition object.
11575  * \param listAddress List of (formatted) E-Mail addresses.
11576  */
11577 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11578         GList *node;
11579         gchar *addr;
11580         node = listAddress;
11581         while( node ) {
11582                 addr = ( gchar * ) node->data;
11583                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11584                 node = g_list_next( node );
11585         }
11586 }
11587
11588 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11589                                     guint action, gboolean opening_multiple)
11590 {
11591         gchar *body = NULL;
11592         GSList *new_msglist = NULL;
11593         MsgInfo *tmp_msginfo = NULL;
11594         gboolean originally_enc = FALSE;
11595         gboolean originally_sig = FALSE;
11596         Compose *compose = NULL;
11597         gchar *s_system = NULL;
11598
11599         cm_return_if_fail(msgview != NULL);
11600
11601         cm_return_if_fail(msginfo_list != NULL);
11602
11603         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11604                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11605                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11606
11607                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11608                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11609                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11610                                                 orig_msginfo, mimeinfo);
11611                         if (tmp_msginfo != NULL) {
11612                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11613
11614                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11615                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11616                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11617
11618                                 tmp_msginfo->folder = orig_msginfo->folder;
11619                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11620                                 if (orig_msginfo->tags) {
11621                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11622                                         tmp_msginfo->folder->tags_dirty = TRUE;
11623                                 }
11624                         }
11625                 }
11626         }
11627
11628         if (!opening_multiple)
11629                 body = messageview_get_selection(msgview);
11630
11631         if (new_msglist) {
11632                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11633                 procmsg_msginfo_free(tmp_msginfo);
11634                 g_slist_free(new_msglist);
11635         } else
11636                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11637
11638         if (compose && originally_enc) {
11639                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11640         }
11641
11642         if (compose && originally_sig && compose->account->default_sign_reply) {
11643                 compose_force_signing(compose, compose->account, s_system);
11644         }
11645         g_free(s_system);
11646         g_free(body);
11647         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11648 }
11649
11650 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11651                                     guint action)
11652 {
11653         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11654         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11655                 GSList *cur = msginfo_list;
11656                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11657                                                "messages. Opening the windows "
11658                                                "could take some time. Do you "
11659                                                "want to continue?"), 
11660                                                g_slist_length(msginfo_list));
11661                 if (g_slist_length(msginfo_list) > 9
11662                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11663                     != G_ALERTALTERNATE) {
11664                         g_free(msg);
11665                         return;
11666                 }
11667                 g_free(msg);
11668                 /* We'll open multiple compose windows */
11669                 /* let the WM place the next windows */
11670                 compose_force_window_origin = FALSE;
11671                 for (; cur; cur = cur->next) {
11672                         GSList tmplist;
11673                         tmplist.data = cur->data;
11674                         tmplist.next = NULL;
11675                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11676                 }
11677                 compose_force_window_origin = TRUE;
11678         } else {
11679                 /* forwarding multiple mails as attachments is done via a
11680                  * single compose window */
11681                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11682         }
11683 }
11684
11685 void compose_check_for_email_account(Compose *compose)
11686 {
11687         PrefsAccount *ac = NULL, *curr = NULL;
11688         GList *list;
11689         
11690         if (!compose)
11691                 return;
11692
11693         if (compose->account && compose->account->protocol == A_NNTP) {
11694                 ac = account_get_cur_account();
11695                 if (ac->protocol == A_NNTP) {
11696                         list = account_get_list();
11697                         
11698                         for( ; list != NULL ; list = g_list_next(list)) {
11699                                 curr = (PrefsAccount *) list->data;
11700                                 if (curr->protocol != A_NNTP) {
11701                                         ac = curr;
11702                                         break;
11703                                 }
11704                         }
11705                 }
11706                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11707                                         ac->account_id); 
11708         }
11709 }
11710
11711 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11712                                 const gchar *address)
11713 {
11714         GSList *msginfo_list = NULL;
11715         gchar *body =  messageview_get_selection(msgview);
11716         Compose *compose;
11717         
11718         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11719         
11720         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11721         compose_check_for_email_account(compose);
11722         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11723         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11724         compose_reply_set_subject(compose, msginfo);
11725
11726         g_free(body);
11727         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11728 }
11729
11730 void compose_set_position(Compose *compose, gint pos)
11731 {
11732         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11733
11734         gtkut_text_view_set_position(text, pos);
11735 }
11736
11737 gboolean compose_search_string(Compose *compose,
11738                                 const gchar *str, gboolean case_sens)
11739 {
11740         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11741
11742         return gtkut_text_view_search_string(text, str, case_sens);
11743 }
11744
11745 gboolean compose_search_string_backward(Compose *compose,
11746                                 const gchar *str, gboolean case_sens)
11747 {
11748         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11749
11750         return gtkut_text_view_search_string_backward(text, str, case_sens);
11751 }
11752
11753 /* allocate a msginfo structure and populate its data from a compose data structure */
11754 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11755 {
11756         MsgInfo *newmsginfo;
11757         GSList *list;
11758         gchar buf[BUFFSIZE];
11759
11760         cm_return_val_if_fail( compose != NULL, NULL );
11761
11762         newmsginfo = procmsg_msginfo_new();
11763
11764         /* date is now */
11765         get_rfc822_date(buf, sizeof(buf));
11766         newmsginfo->date = g_strdup(buf);
11767
11768         /* from */
11769         if (compose->from_name) {
11770                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11771                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11772         }
11773
11774         /* subject */
11775         if (compose->subject_entry)
11776                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11777
11778         /* to, cc, reply-to, newsgroups */
11779         for (list = compose->header_list; list; list = list->next) {
11780                 gchar *header = gtk_editable_get_chars(
11781                                                                 GTK_EDITABLE(
11782                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11783                 gchar *entry = gtk_editable_get_chars(
11784                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11785
11786                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11787                         if ( newmsginfo->to == NULL ) {
11788                                 newmsginfo->to = g_strdup(entry);
11789                         } else if (entry && *entry) {
11790                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11791                                 g_free(newmsginfo->to);
11792                                 newmsginfo->to = tmp;
11793                         }
11794                 } else
11795                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11796                         if ( newmsginfo->cc == NULL ) {
11797                                 newmsginfo->cc = g_strdup(entry);
11798                         } else if (entry && *entry) {
11799                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11800                                 g_free(newmsginfo->cc);
11801                                 newmsginfo->cc = tmp;
11802                         }
11803                 } else
11804                 if ( strcasecmp(header,
11805                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11806                         if ( newmsginfo->newsgroups == NULL ) {
11807                                 newmsginfo->newsgroups = g_strdup(entry);
11808                         } else if (entry && *entry) {
11809                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11810                                 g_free(newmsginfo->newsgroups);
11811                                 newmsginfo->newsgroups = tmp;
11812                         }
11813                 }
11814
11815                 g_free(header);
11816                 g_free(entry);  
11817         }
11818
11819         /* other data is unset */
11820
11821         return newmsginfo;
11822 }
11823
11824 #ifdef USE_ENCHANT
11825 /* update compose's dictionaries from folder dict settings */
11826 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11827                                                 FolderItem *folder_item)
11828 {
11829         cm_return_if_fail(compose != NULL);
11830
11831         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11832                 FolderItemPrefs *prefs = folder_item->prefs;
11833
11834                 if (prefs->enable_default_dictionary)
11835                         gtkaspell_change_dict(compose->gtkaspell,
11836                                         prefs->default_dictionary, FALSE);
11837                 if (folder_item->prefs->enable_default_alt_dictionary)
11838                         gtkaspell_change_alt_dict(compose->gtkaspell,
11839                                         prefs->default_alt_dictionary);
11840                 if (prefs->enable_default_dictionary
11841                         || prefs->enable_default_alt_dictionary)
11842                         compose_spell_menu_changed(compose);
11843         }
11844 }
11845 #endif
11846
11847 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11848 {
11849         Compose *compose = (Compose *)data;
11850
11851         cm_return_if_fail(compose != NULL);
11852
11853         gtk_widget_grab_focus(compose->text);
11854 }
11855
11856 /*
11857  * End of Source.
11858  */