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