Add a mnemonic to compose window's Subject line, allowing alt+s to bring focus there.
[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                                          Compose *compose);
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 = 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(G_OBJECT(notebook), "size_allocate",
7751                          G_CALLBACK(compose_notebook_size_alloc), compose);     
7752         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7753                                G_CALLBACK(compose_edit_size_alloc),
7754                                ruler);
7755         g_signal_connect(G_OBJECT(buffer), "changed",
7756                          G_CALLBACK(compose_changed_cb), compose);
7757         g_signal_connect(G_OBJECT(text), "grab_focus",
7758                          G_CALLBACK(compose_grab_focus_cb), compose);
7759         g_signal_connect(G_OBJECT(buffer), "insert_text",
7760                          G_CALLBACK(text_inserted), compose);
7761         g_signal_connect(G_OBJECT(text), "button_press_event",
7762                          G_CALLBACK(text_clicked), compose);
7763         g_signal_connect(G_OBJECT(text), "popup-menu",
7764                          G_CALLBACK(compose_popup_menu), compose);
7765         g_signal_connect(G_OBJECT(subject_entry), "changed",
7766                         G_CALLBACK(compose_changed_cb), compose);
7767         g_signal_connect(G_OBJECT(subject_entry), "activate",
7768                         G_CALLBACK(compose_subject_entry_activated), compose);
7769
7770         /* drag and drop */
7771         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7772                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7773                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7774         g_signal_connect(G_OBJECT(text), "drag_data_received",
7775                          G_CALLBACK(compose_insert_drag_received_cb),
7776                          compose);
7777         g_signal_connect(G_OBJECT(text), "drag-drop",
7778                          G_CALLBACK(compose_drag_drop),
7779                          compose);
7780         g_signal_connect(G_OBJECT(text), "key-press-event",
7781                          G_CALLBACK(completion_set_focus_to_subject),
7782                          compose);
7783         gtk_widget_show_all(vbox);
7784
7785         /* pane between attach clist and text */
7786         paned = gtk_vpaned_new();
7787         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7788         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
7789         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
7790         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
7791         gtk_widget_show_all(paned);
7792
7793
7794         if (prefs_common.textfont) {
7795                 PangoFontDescription *font_desc;
7796
7797                 font_desc = pango_font_description_from_string
7798                         (prefs_common.textfont);
7799                 if (font_desc) {
7800                         gtk_widget_modify_font(text, font_desc);
7801                         pango_font_description_free(font_desc);
7802                 }
7803         }
7804
7805         gtk_action_group_add_actions(action_group, compose_popup_entries,
7806                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7807         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7808         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7809         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7810         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7811         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7812         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7813         
7814         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7815
7816         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7817         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7818         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7819
7820         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7821
7822         undostruct = undo_init(text);
7823         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7824                                    compose);
7825
7826         address_completion_start(window);
7827
7828         compose->window        = window;
7829         compose->vbox          = vbox;
7830         compose->menubar       = menubar;
7831         compose->handlebox     = handlebox;
7832
7833         compose->vbox2         = vbox2;
7834
7835         compose->paned = paned;
7836
7837         compose->attach_label  = attach_lab2;
7838
7839         compose->notebook      = notebook;
7840         compose->edit_vbox     = edit_vbox;
7841         compose->ruler_hbox    = ruler_hbox;
7842         compose->ruler         = ruler;
7843         compose->scrolledwin   = scrolledwin;
7844         compose->text          = text;
7845
7846         compose->focused_editable = NULL;
7847
7848         compose->popupmenu    = popupmenu;
7849
7850         compose->tmpl_menu = tmpl_menu;
7851
7852         compose->mode = mode;
7853         compose->rmode = mode;
7854
7855         compose->targetinfo = NULL;
7856         compose->replyinfo  = NULL;
7857         compose->fwdinfo    = NULL;
7858
7859         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7860                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7861         
7862         compose->replyto     = NULL;
7863         compose->cc          = NULL;
7864         compose->bcc         = NULL;
7865         compose->followup_to = NULL;
7866
7867         compose->ml_post     = NULL;
7868
7869         compose->inreplyto   = NULL;
7870         compose->references  = NULL;
7871         compose->msgid       = NULL;
7872         compose->boundary    = NULL;
7873
7874         compose->autowrap       = prefs_common.autowrap;
7875         compose->autoindent     = prefs_common.auto_indent;
7876         compose->use_signing    = FALSE;
7877         compose->use_encryption = FALSE;
7878         compose->privacy_system = NULL;
7879
7880         compose->modified = FALSE;
7881
7882         compose->return_receipt = FALSE;
7883
7884         compose->to_list        = NULL;
7885         compose->newsgroup_list = NULL;
7886
7887         compose->undostruct = undostruct;
7888
7889         compose->sig_str = NULL;
7890
7891         compose->exteditor_file    = NULL;
7892         compose->exteditor_pid     = -1;
7893         compose->exteditor_tag     = -1;
7894         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
7895
7896 #if USE_ENCHANT
7897         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7898         if (mode != COMPOSE_REDIRECT) {
7899                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7900                     strcmp(prefs_common.dictionary, "")) {
7901                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7902                                                   prefs_common.alt_dictionary,
7903                                                   conv_get_locale_charset_str(),
7904                                                   prefs_common.misspelled_col,
7905                                                   prefs_common.check_while_typing,
7906                                                   prefs_common.recheck_when_changing_dict,
7907                                                   prefs_common.use_alternate,
7908                                                   prefs_common.use_both_dicts,
7909                                                   GTK_TEXT_VIEW(text),
7910                                                   GTK_WINDOW(compose->window),
7911                                                   compose_dict_changed,
7912                                                   compose_spell_menu_changed,
7913                                                   compose);
7914                         if (!gtkaspell) {
7915                                 alertpanel_error(_("Spell checker could not "
7916                                                 "be started.\n%s"),
7917                                                 gtkaspell_checkers_strerror());
7918                                 gtkaspell_checkers_reset_error();
7919                         } else {
7920                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7921                         }
7922                 }
7923         }
7924         compose->gtkaspell = gtkaspell;
7925         compose_spell_menu_changed(compose);
7926         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7927 #endif
7928
7929         compose_select_account(compose, account, TRUE);
7930
7931         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7932         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7933
7934         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7935                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7936
7937         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7938                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7939         
7940         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7941                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7942
7943         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7944         if (account->protocol != A_NNTP)
7945                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7946                                 prefs_common_translated_header_name("To:"));
7947         else
7948                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7949                                 prefs_common_translated_header_name("Newsgroups:"));
7950
7951 #ifndef USE_NEW_ADDRBOOK
7952         addressbook_set_target_compose(compose);
7953 #endif  
7954         if (mode != COMPOSE_REDIRECT)
7955                 compose_set_template_menu(compose);
7956         else {
7957                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7958         }
7959
7960         compose_list = g_list_append(compose_list, compose);
7961
7962         if (!prefs_common.show_ruler)
7963                 gtk_widget_hide(ruler_hbox);
7964                 
7965         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7966
7967         /* Priority */
7968         compose->priority = PRIORITY_NORMAL;
7969         compose_update_priority_menu_item(compose);
7970
7971         compose_set_out_encoding(compose);
7972         
7973         /* Actions menu */
7974         compose_update_actions_menu(compose);
7975
7976         /* Privacy Systems menu */
7977         compose_update_privacy_systems_menu(compose);
7978
7979         activate_privacy_system(compose, account, TRUE);
7980         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7981         if (batch) {
7982                 gtk_widget_realize(window);
7983         } else {
7984                 gtk_widget_show(window);
7985         }
7986         
7987         return compose;
7988 }
7989
7990 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7991 {
7992         GList *accounts;
7993         GtkWidget *hbox;
7994         GtkWidget *optmenu;
7995         GtkWidget *optmenubox;
7996         GtkListStore *menu;
7997         GtkTreeIter iter;
7998         GtkWidget *from_name = NULL;
7999 #if !(GTK_CHECK_VERSION(2,12,0))
8000         GtkTooltips *tips = compose->tooltips;
8001 #endif
8002
8003         gint num = 0, def_menu = 0;
8004         
8005         accounts = account_get_list();
8006         cm_return_val_if_fail(accounts != NULL, NULL);
8007
8008         optmenubox = gtk_event_box_new();
8009         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8010         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8011
8012         hbox = gtk_hbox_new(FALSE, 6);
8013         from_name = gtk_entry_new();
8014         
8015         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8016                          G_CALLBACK(compose_grab_focus_cb), compose);
8017
8018         for (; accounts != NULL; accounts = accounts->next, num++) {
8019                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8020                 gchar *name, *from = NULL;
8021
8022                 if (ac == compose->account) def_menu = num;
8023
8024                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
8025                                        ac->account_name);
8026                 
8027                 if (ac == compose->account) {
8028                         if (ac->name && *ac->name) {
8029                                 gchar *buf;
8030                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8031                                 from = g_strdup_printf("%s <%s>",
8032                                                        buf, ac->address);
8033                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8034                         } else {
8035                                 from = g_strdup_printf("%s",
8036                                                        ac->address);
8037                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8038                         }
8039                 }
8040                 COMBOBOX_ADD(menu, name, ac->account_id);
8041                 g_free(name);
8042                 g_free(from);
8043         }
8044
8045         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8046
8047         g_signal_connect(G_OBJECT(optmenu), "changed",
8048                         G_CALLBACK(account_activated),
8049                         compose);
8050         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8051                          G_CALLBACK(compose_entry_popup_extend),
8052                          NULL);
8053
8054         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8055         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8056         
8057         CLAWS_SET_TIP(optmenubox,
8058                 _("Account to use for this email"));
8059         CLAWS_SET_TIP(from_name,
8060                 _("Sender address to be used"));
8061
8062         compose->account_combo = optmenu;
8063         compose->from_name = from_name;
8064         
8065         return hbox;
8066 }
8067
8068 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8069 {
8070         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8071         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8072         Compose *compose = (Compose *) data;
8073         if (active) {
8074                 compose->priority = value;
8075         }
8076 }
8077
8078 static void compose_reply_change_mode(Compose *compose,
8079                                     ComposeMode action)
8080 {
8081         gboolean was_modified = compose->modified;
8082
8083         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8084         
8085         cm_return_if_fail(compose->replyinfo != NULL);
8086         
8087         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8088                 ml = TRUE;
8089         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8090                 followup = TRUE;
8091         if (action == COMPOSE_REPLY_TO_ALL)
8092                 all = TRUE;
8093         if (action == COMPOSE_REPLY_TO_SENDER)
8094                 sender = TRUE;
8095         if (action == COMPOSE_REPLY_TO_LIST)
8096                 ml = TRUE;
8097
8098         compose_remove_header_entries(compose);
8099         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8100         if (compose->account->set_autocc && compose->account->auto_cc)
8101                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8102
8103         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8104                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8105         
8106         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8107                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8108         compose_show_first_last_header(compose, TRUE);
8109         compose->modified = was_modified;
8110         compose_set_title(compose);
8111 }
8112
8113 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8114 {
8115         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8116         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8117         Compose *compose = (Compose *) data;
8118         
8119         if (active)
8120                 compose_reply_change_mode(compose, value);
8121 }
8122
8123 static void compose_update_priority_menu_item(Compose * compose)
8124 {
8125         GtkWidget *menuitem = NULL;
8126         switch (compose->priority) {
8127                 case PRIORITY_HIGHEST:
8128                         menuitem = gtk_ui_manager_get_widget
8129                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8130                         break;
8131                 case PRIORITY_HIGH:
8132                         menuitem = gtk_ui_manager_get_widget
8133                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8134                         break;
8135                 case PRIORITY_NORMAL:
8136                         menuitem = gtk_ui_manager_get_widget
8137                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8138                         break;
8139                 case PRIORITY_LOW:
8140                         menuitem = gtk_ui_manager_get_widget
8141                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8142                         break;
8143                 case PRIORITY_LOWEST:
8144                         menuitem = gtk_ui_manager_get_widget
8145                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8146                         break;
8147         }
8148         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8149 }       
8150
8151 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8152 {
8153         Compose *compose = (Compose *) data;
8154         gchar *systemid;
8155         gboolean can_sign = FALSE, can_encrypt = FALSE;
8156
8157         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8158
8159         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8160                 return;
8161
8162         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8163         g_free(compose->privacy_system);
8164         compose->privacy_system = NULL;
8165         if (systemid != NULL) {
8166                 compose->privacy_system = g_strdup(systemid);
8167
8168                 can_sign = privacy_system_can_sign(systemid);
8169                 can_encrypt = privacy_system_can_encrypt(systemid);
8170         }
8171
8172         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8173
8174         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8175         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8176 }
8177
8178 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8179 {
8180         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8181         GtkWidget *menuitem = NULL;
8182         GList *children, *amenu;
8183         gboolean can_sign = FALSE, can_encrypt = FALSE;
8184         gboolean found = FALSE;
8185
8186         if (compose->privacy_system != NULL) {
8187                 gchar *systemid;
8188                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8189                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8190                 cm_return_if_fail(menuitem != NULL);
8191
8192                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8193                 amenu = children;
8194                 menuitem = NULL;
8195                 while (amenu != NULL) {
8196                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8197                         if (systemid != NULL) {
8198                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8199                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8200                                         menuitem = GTK_WIDGET(amenu->data);
8201
8202                                         can_sign = privacy_system_can_sign(systemid);
8203                                         can_encrypt = privacy_system_can_encrypt(systemid);
8204                                         found = TRUE;
8205                                         break;
8206                                 } 
8207                         } else if (strlen(compose->privacy_system) == 0 && 
8208                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8209                                         menuitem = GTK_WIDGET(amenu->data);
8210
8211                                         can_sign = FALSE;
8212                                         can_encrypt = FALSE;
8213                                         found = TRUE;
8214                                         break;
8215                         }
8216
8217                         amenu = amenu->next;
8218                 }
8219                 g_list_free(children);
8220                 if (menuitem != NULL)
8221                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8222                 
8223                 if (warn && !found && strlen(compose->privacy_system)) {
8224                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8225                                   "will not be able to sign or encrypt this message."),
8226                                   compose->privacy_system);
8227                 }
8228         } 
8229
8230         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8231         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8232 }       
8233  
8234 static void compose_set_out_encoding(Compose *compose)
8235 {
8236         CharSet out_encoding;
8237         const gchar *branch = NULL;
8238         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8239
8240         switch(out_encoding) {
8241                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8242                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8243                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8244                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8245                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8246                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8247                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8248                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8249                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8250                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8251                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8252                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8253                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8254                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8255                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8256                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8257                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8258                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8259                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8260                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8261                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8262                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8263                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8264                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8265                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8266                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8267                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8268                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8269                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8270                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8271                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8272                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8273                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8274         }
8275         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8276 }
8277
8278 static void compose_set_template_menu(Compose *compose)
8279 {
8280         GSList *tmpl_list, *cur;
8281         GtkWidget *menu;
8282         GtkWidget *item;
8283
8284         tmpl_list = template_get_config();
8285
8286         menu = gtk_menu_new();
8287
8288         gtk_menu_set_accel_group (GTK_MENU (menu), 
8289                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8290         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8291                 Template *tmpl = (Template *)cur->data;
8292                 gchar *accel_path = NULL;
8293                 item = gtk_menu_item_new_with_label(tmpl->name);
8294                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8295                 g_signal_connect(G_OBJECT(item), "activate",
8296                                  G_CALLBACK(compose_template_activate_cb),
8297                                  compose);
8298                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8299                 gtk_widget_show(item);
8300                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8301                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8302                 g_free(accel_path);
8303         }
8304
8305         gtk_widget_show(menu);
8306         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8307 }
8308
8309 void compose_update_actions_menu(Compose *compose)
8310 {
8311         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8312 }
8313
8314 static void compose_update_privacy_systems_menu(Compose *compose)
8315 {
8316         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8317         GSList *systems, *cur;
8318         GtkWidget *widget;
8319         GtkWidget *system_none;
8320         GSList *group;
8321         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8322         GtkWidget *privacy_menu = gtk_menu_new();
8323
8324         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8325         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8326
8327         g_signal_connect(G_OBJECT(system_none), "activate",
8328                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8329
8330         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8331         gtk_widget_show(system_none);
8332
8333         systems = privacy_get_system_ids();
8334         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8335                 gchar *systemid = cur->data;
8336
8337                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8338                 widget = gtk_radio_menu_item_new_with_label(group,
8339                         privacy_system_get_name(systemid));
8340                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8341                                        g_strdup(systemid), g_free);
8342                 g_signal_connect(G_OBJECT(widget), "activate",
8343                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8344
8345                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8346                 gtk_widget_show(widget);
8347                 g_free(systemid);
8348         }
8349         g_slist_free(systems);
8350         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8351         gtk_widget_show_all(privacy_menu);
8352         gtk_widget_show_all(privacy_menuitem);
8353 }
8354
8355 void compose_reflect_prefs_all(void)
8356 {
8357         GList *cur;
8358         Compose *compose;
8359
8360         for (cur = compose_list; cur != NULL; cur = cur->next) {
8361                 compose = (Compose *)cur->data;
8362                 compose_set_template_menu(compose);
8363         }
8364 }
8365
8366 void compose_reflect_prefs_pixmap_theme(void)
8367 {
8368         GList *cur;
8369         Compose *compose;
8370
8371         for (cur = compose_list; cur != NULL; cur = cur->next) {
8372                 compose = (Compose *)cur->data;
8373                 toolbar_update(TOOLBAR_COMPOSE, compose);
8374         }
8375 }
8376
8377 static const gchar *compose_quote_char_from_context(Compose *compose)
8378 {
8379         const gchar *qmark = NULL;
8380
8381         cm_return_val_if_fail(compose != NULL, NULL);
8382
8383         switch (compose->mode) {
8384                 /* use forward-specific quote char */
8385                 case COMPOSE_FORWARD:
8386                 case COMPOSE_FORWARD_AS_ATTACH:
8387                 case COMPOSE_FORWARD_INLINE:
8388                         if (compose->folder && compose->folder->prefs &&
8389                                         compose->folder->prefs->forward_with_format)
8390                                 qmark = compose->folder->prefs->forward_quotemark;
8391                         else if (compose->account->forward_with_format)
8392                                 qmark = compose->account->forward_quotemark;
8393                         else
8394                                 qmark = prefs_common.fw_quotemark;
8395                         break;
8396
8397                 /* use reply-specific quote char in all other modes */
8398                 default:
8399                         if (compose->folder && compose->folder->prefs &&
8400                                         compose->folder->prefs->reply_with_format)
8401                                 qmark = compose->folder->prefs->reply_quotemark;
8402                         else if (compose->account->reply_with_format)
8403                                 qmark = compose->account->reply_quotemark;
8404                         else
8405                                 qmark = prefs_common.quotemark;
8406                         break;
8407         }
8408
8409         if (qmark == NULL || *qmark == '\0')
8410                 qmark = "> ";
8411
8412         return qmark;
8413 }
8414
8415 static void compose_template_apply(Compose *compose, Template *tmpl,
8416                                    gboolean replace)
8417 {
8418         GtkTextView *text;
8419         GtkTextBuffer *buffer;
8420         GtkTextMark *mark;
8421         GtkTextIter iter;
8422         const gchar *qmark;
8423         gchar *parsed_str = NULL;
8424         gint cursor_pos = 0;
8425         const gchar *err_msg = _("The body of the template has an error at line %d.");
8426         if (!tmpl) return;
8427
8428         /* process the body */
8429
8430         text = GTK_TEXT_VIEW(compose->text);
8431         buffer = gtk_text_view_get_buffer(text);
8432
8433         if (tmpl->value) {
8434                 qmark = compose_quote_char_from_context(compose);
8435
8436                 if (compose->replyinfo != NULL) {
8437
8438                         if (replace)
8439                                 gtk_text_buffer_set_text(buffer, "", -1);
8440                         mark = gtk_text_buffer_get_insert(buffer);
8441                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8442
8443                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8444                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8445
8446                 } else if (compose->fwdinfo != NULL) {
8447
8448                         if (replace)
8449                                 gtk_text_buffer_set_text(buffer, "", -1);
8450                         mark = gtk_text_buffer_get_insert(buffer);
8451                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8452
8453                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8454                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8455
8456                 } else {
8457                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8458
8459                         GtkTextIter start, end;
8460                         gchar *tmp = NULL;
8461
8462                         gtk_text_buffer_get_start_iter(buffer, &start);
8463                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8464                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8465
8466                         /* clear the buffer now */
8467                         if (replace)
8468                                 gtk_text_buffer_set_text(buffer, "", -1);
8469
8470                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8471                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8472                         procmsg_msginfo_free( dummyinfo );
8473
8474                         g_free( tmp );
8475                 } 
8476         } else {
8477                 if (replace)
8478                         gtk_text_buffer_set_text(buffer, "", -1);
8479                 mark = gtk_text_buffer_get_insert(buffer);
8480                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8481         }       
8482
8483         if (replace && parsed_str && compose->account->auto_sig)
8484                 compose_insert_sig(compose, FALSE);
8485
8486         if (replace && parsed_str) {
8487                 gtk_text_buffer_get_start_iter(buffer, &iter);
8488                 gtk_text_buffer_place_cursor(buffer, &iter);
8489         }
8490         
8491         if (parsed_str) {
8492                 cursor_pos = quote_fmt_get_cursor_pos();
8493                 compose->set_cursor_pos = cursor_pos;
8494                 if (cursor_pos == -1)
8495                         cursor_pos = 0;
8496                 gtk_text_buffer_get_start_iter(buffer, &iter);
8497                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8498                 gtk_text_buffer_place_cursor(buffer, &iter);
8499         }
8500
8501         /* process the other fields */
8502
8503         compose_template_apply_fields(compose, tmpl);
8504         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8505         quote_fmt_reset_vartable();
8506         compose_changed_cb(NULL, compose);
8507
8508 #ifdef USE_ENCHANT
8509         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8510                 gtkaspell_highlight_all(compose->gtkaspell);
8511 #endif
8512 }
8513
8514 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8515 {
8516         MsgInfo* dummyinfo = NULL;
8517         MsgInfo *msginfo = NULL;
8518         gchar *buf = NULL;
8519
8520         if (compose->replyinfo != NULL)
8521                 msginfo = compose->replyinfo;
8522         else if (compose->fwdinfo != NULL)
8523                 msginfo = compose->fwdinfo;
8524         else {
8525                 dummyinfo = compose_msginfo_new_from_compose(compose);
8526                 msginfo = dummyinfo;
8527         }
8528
8529         if (tmpl->from && *tmpl->from != '\0') {
8530 #ifdef USE_ENCHANT
8531                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8532                                 compose->gtkaspell);
8533 #else
8534                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8535 #endif
8536                 quote_fmt_scan_string(tmpl->from);
8537                 quote_fmt_parse();
8538
8539                 buf = quote_fmt_get_buffer();
8540                 if (buf == NULL) {
8541                         alertpanel_error(_("Template From format error."));
8542                 } else {
8543                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8544                 }
8545         }
8546
8547         if (tmpl->to && *tmpl->to != '\0') {
8548 #ifdef USE_ENCHANT
8549                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8550                                 compose->gtkaspell);
8551 #else
8552                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8553 #endif
8554                 quote_fmt_scan_string(tmpl->to);
8555                 quote_fmt_parse();
8556
8557                 buf = quote_fmt_get_buffer();
8558                 if (buf == NULL) {
8559                         alertpanel_error(_("Template To format error."));
8560                 } else {
8561                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8562                 }
8563         }
8564
8565         if (tmpl->cc && *tmpl->cc != '\0') {
8566 #ifdef USE_ENCHANT
8567                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8568                                 compose->gtkaspell);
8569 #else
8570                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8571 #endif
8572                 quote_fmt_scan_string(tmpl->cc);
8573                 quote_fmt_parse();
8574
8575                 buf = quote_fmt_get_buffer();
8576                 if (buf == NULL) {
8577                         alertpanel_error(_("Template Cc format error."));
8578                 } else {
8579                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8580                 }
8581         }
8582
8583         if (tmpl->bcc && *tmpl->bcc != '\0') {
8584 #ifdef USE_ENCHANT
8585                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8586                                 compose->gtkaspell);
8587 #else
8588                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8589 #endif
8590                 quote_fmt_scan_string(tmpl->bcc);
8591                 quote_fmt_parse();
8592
8593                 buf = quote_fmt_get_buffer();
8594                 if (buf == NULL) {
8595                         alertpanel_error(_("Template Bcc format error."));
8596                 } else {
8597                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8598                 }
8599         }
8600
8601         /* process the subject */
8602         if (tmpl->subject && *tmpl->subject != '\0') {
8603 #ifdef USE_ENCHANT
8604                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8605                                 compose->gtkaspell);
8606 #else
8607                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8608 #endif
8609                 quote_fmt_scan_string(tmpl->subject);
8610                 quote_fmt_parse();
8611
8612                 buf = quote_fmt_get_buffer();
8613                 if (buf == NULL) {
8614                         alertpanel_error(_("Template subject format error."));
8615                 } else {
8616                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8617                 }
8618         }
8619
8620         procmsg_msginfo_free( dummyinfo );
8621 }
8622
8623 static void compose_destroy(Compose *compose)
8624 {
8625         GtkAllocation allocation;
8626         GtkTextBuffer *buffer;
8627         GtkClipboard *clipboard;
8628
8629         compose_list = g_list_remove(compose_list, compose);
8630
8631         if (compose->updating) {
8632                 debug_print("danger, not destroying anything now\n");
8633                 compose->deferred_destroy = TRUE;
8634                 return;
8635         }
8636         /* NOTE: address_completion_end() does nothing with the window
8637          * however this may change. */
8638         address_completion_end(compose->window);
8639
8640         slist_free_strings_full(compose->to_list);
8641         slist_free_strings_full(compose->newsgroup_list);
8642         slist_free_strings_full(compose->header_list);
8643
8644         slist_free_strings_full(extra_headers);
8645         extra_headers = NULL;
8646
8647         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8648
8649         g_hash_table_destroy(compose->email_hashtable);
8650
8651         procmsg_msginfo_free(compose->targetinfo);
8652         procmsg_msginfo_free(compose->replyinfo);
8653         procmsg_msginfo_free(compose->fwdinfo);
8654
8655         g_free(compose->replyto);
8656         g_free(compose->cc);
8657         g_free(compose->bcc);
8658         g_free(compose->newsgroups);
8659         g_free(compose->followup_to);
8660
8661         g_free(compose->ml_post);
8662
8663         g_free(compose->inreplyto);
8664         g_free(compose->references);
8665         g_free(compose->msgid);
8666         g_free(compose->boundary);
8667
8668         g_free(compose->redirect_filename);
8669         if (compose->undostruct)
8670                 undo_destroy(compose->undostruct);
8671
8672         g_free(compose->sig_str);
8673
8674         g_free(compose->exteditor_file);
8675
8676         g_free(compose->orig_charset);
8677
8678         g_free(compose->privacy_system);
8679
8680 #ifndef USE_NEW_ADDRBOOK
8681         if (addressbook_get_target_compose() == compose)
8682                 addressbook_set_target_compose(NULL);
8683 #endif
8684 #if USE_ENCHANT
8685         if (compose->gtkaspell) {
8686                 gtkaspell_delete(compose->gtkaspell);
8687                 compose->gtkaspell = NULL;
8688         }
8689 #endif
8690
8691         if (!compose->batch) {
8692                 gtk_widget_get_allocation(compose->window, &allocation);
8693                 prefs_common.compose_width = allocation.width;
8694                 prefs_common.compose_height = allocation.height;
8695         }
8696
8697         if (!gtk_widget_get_parent(compose->paned))
8698                 gtk_widget_destroy(compose->paned);
8699         gtk_widget_destroy(compose->popupmenu);
8700
8701         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8702         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8703         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8704
8705         gtk_widget_destroy(compose->window);
8706         toolbar_destroy(compose->toolbar);
8707         g_free(compose->toolbar);
8708         cm_mutex_free(compose->mutex);
8709         g_free(compose);
8710 }
8711
8712 static void compose_attach_info_free(AttachInfo *ainfo)
8713 {
8714         g_free(ainfo->file);
8715         g_free(ainfo->content_type);
8716         g_free(ainfo->name);
8717         g_free(ainfo->charset);
8718         g_free(ainfo);
8719 }
8720
8721 static void compose_attach_update_label(Compose *compose)
8722 {
8723         GtkTreeIter iter;
8724         gint i = 1;
8725         gchar *text;
8726         GtkTreeModel *model;
8727         
8728         if(compose == NULL)
8729                 return;
8730                 
8731         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8732         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8733                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8734                 return;
8735         }
8736         
8737         while(gtk_tree_model_iter_next(model, &iter))
8738                 i++;
8739         
8740         text = g_strdup_printf("(%d)", i);
8741         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8742         g_free(text);
8743 }
8744
8745 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8746 {
8747         Compose *compose = (Compose *)data;
8748         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8749         GtkTreeSelection *selection;
8750         GList *sel, *cur;
8751         GtkTreeModel *model;
8752
8753         selection = gtk_tree_view_get_selection(tree_view);
8754         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8755
8756         if (!sel) 
8757                 return;
8758
8759         for (cur = sel; cur != NULL; cur = cur->next) {
8760                 GtkTreePath *path = cur->data;
8761                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8762                                                 (model, cur->data);
8763                 cur->data = ref;
8764                 gtk_tree_path_free(path);
8765         }
8766
8767         for (cur = sel; cur != NULL; cur = cur->next) {
8768                 GtkTreeRowReference *ref = cur->data;
8769                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8770                 GtkTreeIter iter;
8771
8772                 if (gtk_tree_model_get_iter(model, &iter, path))
8773                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8774                 
8775                 gtk_tree_path_free(path);
8776                 gtk_tree_row_reference_free(ref);
8777         }
8778
8779         g_list_free(sel);
8780         compose_attach_update_label(compose);
8781 }
8782
8783 static struct _AttachProperty
8784 {
8785         GtkWidget *window;
8786         GtkWidget *mimetype_entry;
8787         GtkWidget *encoding_optmenu;
8788         GtkWidget *path_entry;
8789         GtkWidget *filename_entry;
8790         GtkWidget *ok_btn;
8791         GtkWidget *cancel_btn;
8792 } attach_prop;
8793
8794 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8795 {       
8796         gtk_tree_path_free((GtkTreePath *)ptr);
8797 }
8798
8799 static void compose_attach_property(GtkAction *action, gpointer data)
8800 {
8801         Compose *compose = (Compose *)data;
8802         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8803         AttachInfo *ainfo;
8804         GtkComboBox *optmenu;
8805         GtkTreeSelection *selection;
8806         GList *sel;
8807         GtkTreeModel *model;
8808         GtkTreeIter iter;
8809         GtkTreePath *path;
8810         static gboolean cancelled;
8811
8812         /* only if one selected */
8813         selection = gtk_tree_view_get_selection(tree_view);
8814         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8815                 return;
8816
8817         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8818         if (!sel)
8819                 return;
8820
8821         path = (GtkTreePath *) sel->data;
8822         gtk_tree_model_get_iter(model, &iter, path);
8823         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8824         
8825         if (!ainfo) {
8826                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8827                 g_list_free(sel);
8828                 return;
8829         }               
8830         g_list_free(sel);
8831
8832         if (!attach_prop.window)
8833                 compose_attach_property_create(&cancelled);
8834         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8835         gtk_widget_grab_focus(attach_prop.ok_btn);
8836         gtk_widget_show(attach_prop.window);
8837         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8838                         GTK_WINDOW(compose->window));
8839
8840         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8841         if (ainfo->encoding == ENC_UNKNOWN)
8842                 combobox_select_by_data(optmenu, ENC_BASE64);
8843         else
8844                 combobox_select_by_data(optmenu, ainfo->encoding);
8845
8846         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8847                            ainfo->content_type ? ainfo->content_type : "");
8848         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8849                            ainfo->file ? ainfo->file : "");
8850         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8851                            ainfo->name ? ainfo->name : "");
8852
8853         for (;;) {
8854                 const gchar *entry_text;
8855                 gchar *text;
8856                 gchar *cnttype = NULL;
8857                 gchar *file = NULL;
8858                 off_t size = 0;
8859
8860                 cancelled = FALSE;
8861                 gtk_main();
8862
8863                 gtk_widget_hide(attach_prop.window);
8864                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8865                 
8866                 if (cancelled) 
8867                         break;
8868
8869                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8870                 if (*entry_text != '\0') {
8871                         gchar *p;
8872
8873                         text = g_strstrip(g_strdup(entry_text));
8874                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8875                                 cnttype = g_strdup(text);
8876                                 g_free(text);
8877                         } else {
8878                                 alertpanel_error(_("Invalid MIME type."));
8879                                 g_free(text);
8880                                 continue;
8881                         }
8882                 }
8883
8884                 ainfo->encoding = combobox_get_active_data(optmenu);
8885
8886                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8887                 if (*entry_text != '\0') {
8888                         if (is_file_exist(entry_text) &&
8889                             (size = get_file_size(entry_text)) > 0)
8890                                 file = g_strdup(entry_text);
8891                         else {
8892                                 alertpanel_error
8893                                         (_("File doesn't exist or is empty."));
8894                                 g_free(cnttype);
8895                                 continue;
8896                         }
8897                 }
8898
8899                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8900                 if (*entry_text != '\0') {
8901                         g_free(ainfo->name);
8902                         ainfo->name = g_strdup(entry_text);
8903                 }
8904
8905                 if (cnttype) {
8906                         g_free(ainfo->content_type);
8907                         ainfo->content_type = cnttype;
8908                 }
8909                 if (file) {
8910                         g_free(ainfo->file);
8911                         ainfo->file = file;
8912                 }
8913                 if (size)
8914                         ainfo->size = (goffset)size;
8915
8916                 /* update tree store */
8917                 text = to_human_readable(ainfo->size);
8918                 gtk_tree_model_get_iter(model, &iter, path);
8919                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8920                                    COL_MIMETYPE, ainfo->content_type,
8921                                    COL_SIZE, text,
8922                                    COL_NAME, ainfo->name,
8923                                    COL_CHARSET, ainfo->charset,
8924                                    -1);
8925                 
8926                 break;
8927         }
8928
8929         gtk_tree_path_free(path);
8930 }
8931
8932 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8933 { \
8934         label = gtk_label_new(str); \
8935         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8936                          GTK_FILL, 0, 0, 0); \
8937         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8938  \
8939         entry = gtk_entry_new(); \
8940         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8941                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8942 }
8943
8944 static void compose_attach_property_create(gboolean *cancelled)
8945 {
8946         GtkWidget *window;
8947         GtkWidget *vbox;
8948         GtkWidget *table;
8949         GtkWidget *label;
8950         GtkWidget *mimetype_entry;
8951         GtkWidget *hbox;
8952         GtkWidget *optmenu;
8953         GtkListStore *optmenu_menu;
8954         GtkWidget *path_entry;
8955         GtkWidget *filename_entry;
8956         GtkWidget *hbbox;
8957         GtkWidget *ok_btn;
8958         GtkWidget *cancel_btn;
8959         GList     *mime_type_list, *strlist;
8960         GtkTreeIter iter;
8961
8962         debug_print("Creating attach_property window...\n");
8963
8964         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8965         gtk_widget_set_size_request(window, 480, -1);
8966         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8967         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8968         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8969         g_signal_connect(G_OBJECT(window), "delete_event",
8970                          G_CALLBACK(attach_property_delete_event),
8971                          cancelled);
8972         g_signal_connect(G_OBJECT(window), "key_press_event",
8973                          G_CALLBACK(attach_property_key_pressed),
8974                          cancelled);
8975
8976         vbox = gtk_vbox_new(FALSE, 8);
8977         gtk_container_add(GTK_CONTAINER(window), vbox);
8978
8979         table = gtk_table_new(4, 2, FALSE);
8980         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8981         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8982         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8983
8984         label = gtk_label_new(_("MIME type")); 
8985         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8986                          GTK_FILL, 0, 0, 0); 
8987         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8988 #if !GTK_CHECK_VERSION(2, 24, 0)
8989         mimetype_entry = gtk_combo_box_entry_new_text(); 
8990 #else
8991         mimetype_entry = gtk_combo_box_text_new_with_entry();
8992 #endif
8993         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8994                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8995                          
8996         /* stuff with list */
8997         mime_type_list = procmime_get_mime_type_list();
8998         strlist = NULL;
8999         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9000                 MimeType *type = (MimeType *) mime_type_list->data;
9001                 gchar *tmp;
9002
9003                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9004
9005                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9006                         g_free(tmp);
9007                 else
9008                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9009                                         (GCompareFunc)strcmp2);
9010         }
9011
9012         for (mime_type_list = strlist; mime_type_list != NULL; 
9013                 mime_type_list = mime_type_list->next) {
9014 #if !GTK_CHECK_VERSION(2, 24, 0)
9015                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9016 #else
9017                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9018 #endif
9019                 g_free(mime_type_list->data);
9020         }
9021         g_list_free(strlist);
9022         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9023         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9024
9025         label = gtk_label_new(_("Encoding"));
9026         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9027                          GTK_FILL, 0, 0, 0);
9028         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9029
9030         hbox = gtk_hbox_new(FALSE, 0);
9031         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9032                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9033
9034         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9035         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9036
9037         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9038         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9039         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9040         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9041         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9042
9043         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9044
9045         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9046         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9047
9048         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9049                                       &ok_btn, GTK_STOCK_OK,
9050                                       NULL, NULL);
9051         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9052         gtk_widget_grab_default(ok_btn);
9053
9054         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9055                          G_CALLBACK(attach_property_ok),
9056                          cancelled);
9057         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9058                          G_CALLBACK(attach_property_cancel),
9059                          cancelled);
9060
9061         gtk_widget_show_all(vbox);
9062
9063         attach_prop.window           = window;
9064         attach_prop.mimetype_entry   = mimetype_entry;
9065         attach_prop.encoding_optmenu = optmenu;
9066         attach_prop.path_entry       = path_entry;
9067         attach_prop.filename_entry   = filename_entry;
9068         attach_prop.ok_btn           = ok_btn;
9069         attach_prop.cancel_btn       = cancel_btn;
9070 }
9071
9072 #undef SET_LABEL_AND_ENTRY
9073
9074 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9075 {
9076         *cancelled = FALSE;
9077         gtk_main_quit();
9078 }
9079
9080 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9081 {
9082         *cancelled = TRUE;
9083         gtk_main_quit();
9084 }
9085
9086 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9087                                          gboolean *cancelled)
9088 {
9089         *cancelled = TRUE;
9090         gtk_main_quit();
9091
9092         return TRUE;
9093 }
9094
9095 static gboolean attach_property_key_pressed(GtkWidget *widget,
9096                                             GdkEventKey *event,
9097                                             gboolean *cancelled)
9098 {
9099         if (event && event->keyval == GDK_KEY_Escape) {
9100                 *cancelled = TRUE;
9101                 gtk_main_quit();
9102         }
9103         if (event && event->keyval == GDK_KEY_Return) {
9104                 *cancelled = FALSE;
9105                 gtk_main_quit();
9106                 return TRUE;
9107         }
9108         return FALSE;
9109 }
9110
9111 static void compose_exec_ext_editor(Compose *compose)
9112 {
9113 #ifdef G_OS_UNIX
9114         gchar *tmp;
9115         pid_t pid;
9116         gint pipe_fds[2];
9117
9118         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9119                               G_DIR_SEPARATOR, compose);
9120
9121         if (pipe(pipe_fds) < 0) {
9122                 perror("pipe");
9123                 g_free(tmp);
9124                 return;
9125         }
9126
9127         if ((pid = fork()) < 0) {
9128                 perror("fork");
9129                 g_free(tmp);
9130                 return;
9131         }
9132
9133         if (pid != 0) {
9134                 /* close the write side of the pipe */
9135                 close(pipe_fds[1]);
9136
9137                 compose->exteditor_file    = g_strdup(tmp);
9138                 compose->exteditor_pid     = pid;
9139
9140                 compose_set_ext_editor_sensitive(compose, FALSE);
9141
9142 #ifndef G_OS_WIN32
9143                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9144 #else
9145                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9146 #endif
9147                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9148                                                         G_IO_IN,
9149                                                         compose_input_cb,
9150                                                         compose);
9151         } else {        /* process-monitoring process */
9152                 pid_t pid_ed;
9153
9154                 if (setpgid(0, 0))
9155                         perror("setpgid");
9156
9157                 /* close the read side of the pipe */
9158                 close(pipe_fds[0]);
9159
9160                 if (compose_write_body_to_file(compose, tmp) < 0) {
9161                         fd_write_all(pipe_fds[1], "2\n", 2);
9162                         _exit(1);
9163                 }
9164
9165                 pid_ed = compose_exec_ext_editor_real(tmp);
9166                 if (pid_ed < 0) {
9167                         fd_write_all(pipe_fds[1], "1\n", 2);
9168                         _exit(1);
9169                 }
9170
9171                 /* wait until editor is terminated */
9172                 waitpid(pid_ed, NULL, 0);
9173
9174                 fd_write_all(pipe_fds[1], "0\n", 2);
9175
9176                 close(pipe_fds[1]);
9177                 _exit(0);
9178         }
9179
9180         g_free(tmp);
9181 #endif /* G_OS_UNIX */
9182 }
9183
9184 #ifdef G_OS_UNIX
9185 static gint compose_exec_ext_editor_real(const gchar *file)
9186 {
9187         gchar buf[1024];
9188         gchar *p;
9189         gchar **cmdline;
9190         pid_t pid;
9191
9192         cm_return_val_if_fail(file != NULL, -1);
9193
9194         if ((pid = fork()) < 0) {
9195                 perror("fork");
9196                 return -1;
9197         }
9198
9199         if (pid != 0) return pid;
9200
9201         /* grandchild process */
9202
9203         if (setpgid(0, getppid()))
9204                 perror("setpgid");
9205
9206         if (prefs_common_get_ext_editor_cmd() &&
9207             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9208             *(p + 1) == 's' && !strchr(p + 2, '%')) {
9209                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9210         } else {
9211                 if (prefs_common_get_ext_editor_cmd())
9212                         g_warning("External editor command-line is invalid: '%s'\n",
9213                                   prefs_common_get_ext_editor_cmd());
9214                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9215         }
9216
9217         cmdline = strsplit_with_quote(buf, " ", 1024);
9218         execvp(cmdline[0], cmdline);
9219
9220         perror("execvp");
9221         g_strfreev(cmdline);
9222
9223         _exit(1);
9224 }
9225
9226 static gboolean compose_ext_editor_kill(Compose *compose)
9227 {
9228         pid_t pgid = compose->exteditor_pid * -1;
9229         gint ret;
9230
9231         ret = kill(pgid, 0);
9232
9233         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9234                 AlertValue val;
9235                 gchar *msg;
9236
9237                 msg = g_strdup_printf
9238                         (_("The external editor is still working.\n"
9239                            "Force terminating the process?\n"
9240                            "process group id: %d"), -pgid);
9241                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9242                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9243                         
9244                 g_free(msg);
9245
9246                 if (val == G_ALERTALTERNATE) {
9247                         g_source_remove(compose->exteditor_tag);
9248                         g_io_channel_shutdown(compose->exteditor_ch,
9249                                               FALSE, NULL);
9250                         g_io_channel_unref(compose->exteditor_ch);
9251
9252                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9253                         waitpid(compose->exteditor_pid, NULL, 0);
9254
9255                         g_warning("Terminated process group id: %d", -pgid);
9256                         g_warning("Temporary file: %s",
9257                                   compose->exteditor_file);
9258
9259                         compose_set_ext_editor_sensitive(compose, TRUE);
9260
9261                         g_free(compose->exteditor_file);
9262                         compose->exteditor_file    = NULL;
9263                         compose->exteditor_pid     = -1;
9264                         compose->exteditor_ch      = NULL;
9265                         compose->exteditor_tag     = -1;
9266                 } else
9267                         return FALSE;
9268         }
9269
9270         return TRUE;
9271 }
9272
9273 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9274                                  gpointer data)
9275 {
9276         gchar buf[3] = "3";
9277         Compose *compose = (Compose *)data;
9278         gsize bytes_read;
9279
9280         debug_print("Compose: input from monitoring process\n");
9281
9282         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9283
9284         g_io_channel_shutdown(source, FALSE, NULL);
9285         g_io_channel_unref(source);
9286
9287         waitpid(compose->exteditor_pid, NULL, 0);
9288
9289         if (buf[0] == '0') {            /* success */
9290                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9291                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9292
9293                 gtk_text_buffer_set_text(buffer, "", -1);
9294                 compose_insert_file(compose, compose->exteditor_file);
9295                 compose_changed_cb(NULL, compose);
9296                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9297
9298                 if (claws_unlink(compose->exteditor_file) < 0)
9299                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9300         } else if (buf[0] == '1') {     /* failed */
9301                 g_warning("Couldn't exec external editor\n");
9302                 if (claws_unlink(compose->exteditor_file) < 0)
9303                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9304         } else if (buf[0] == '2') {
9305                 g_warning("Couldn't write to file\n");
9306         } else if (buf[0] == '3') {
9307                 g_warning("Pipe read failed\n");
9308         }
9309
9310         compose_set_ext_editor_sensitive(compose, TRUE);
9311
9312         g_free(compose->exteditor_file);
9313         compose->exteditor_file    = NULL;
9314         compose->exteditor_pid     = -1;
9315         compose->exteditor_ch      = NULL;
9316         compose->exteditor_tag     = -1;
9317
9318         return FALSE;
9319 }
9320
9321 static void compose_set_ext_editor_sensitive(Compose *compose,
9322                                              gboolean sensitive)
9323 {
9324         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9325         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9326         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9327         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9328         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", sensitive);
9329         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9330         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9331         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9332
9333         gtk_widget_set_sensitive(compose->text,                       sensitive);
9334         if (compose->toolbar->send_btn)
9335                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9336         if (compose->toolbar->sendl_btn)
9337                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9338         if (compose->toolbar->draft_btn)
9339                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9340         if (compose->toolbar->insert_btn)
9341                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9342         if (compose->toolbar->sig_btn)
9343                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9344         if (compose->toolbar->exteditor_btn)
9345                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9346         if (compose->toolbar->linewrap_current_btn)
9347                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9348         if (compose->toolbar->linewrap_all_btn)
9349                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9350 }
9351 #endif /* G_OS_UNIX */
9352
9353 /**
9354  * compose_undo_state_changed:
9355  *
9356  * Change the sensivity of the menuentries undo and redo
9357  **/
9358 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9359                                        gint redo_state, gpointer data)
9360 {
9361         Compose *compose = (Compose *)data;
9362
9363         switch (undo_state) {
9364         case UNDO_STATE_TRUE:
9365                 if (!undostruct->undo_state) {
9366                         undostruct->undo_state = TRUE;
9367                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9368                 }
9369                 break;
9370         case UNDO_STATE_FALSE:
9371                 if (undostruct->undo_state) {
9372                         undostruct->undo_state = FALSE;
9373                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9374                 }
9375                 break;
9376         case UNDO_STATE_UNCHANGED:
9377                 break;
9378         case UNDO_STATE_REFRESH:
9379                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9380                 break;
9381         default:
9382                 g_warning("Undo state not recognized");
9383                 break;
9384         }
9385
9386         switch (redo_state) {
9387         case UNDO_STATE_TRUE:
9388                 if (!undostruct->redo_state) {
9389                         undostruct->redo_state = TRUE;
9390                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9391                 }
9392                 break;
9393         case UNDO_STATE_FALSE:
9394                 if (undostruct->redo_state) {
9395                         undostruct->redo_state = FALSE;
9396                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9397                 }
9398                 break;
9399         case UNDO_STATE_UNCHANGED:
9400                 break;
9401         case UNDO_STATE_REFRESH:
9402                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9403                 break;
9404         default:
9405                 g_warning("Redo state not recognized");
9406                 break;
9407         }
9408 }
9409
9410 /* callback functions */
9411
9412 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9413                                         GtkAllocation *allocation,
9414                                         Compose *compose)
9415 {
9416         prefs_common.compose_notebook_height = allocation->height;
9417 }
9418
9419 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9420  * includes "non-client" (windows-izm) in calculation, so this calculation
9421  * may not be accurate.
9422  */
9423 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9424                                         GtkAllocation *allocation,
9425                                         GtkSHRuler *shruler)
9426 {
9427         if (prefs_common.show_ruler) {
9428                 gint char_width = 0, char_height = 0;
9429                 gint line_width_in_chars;
9430
9431                 gtkut_get_font_size(GTK_WIDGET(widget),
9432                                     &char_width, &char_height);
9433                 line_width_in_chars =
9434                         (allocation->width - allocation->x) / char_width;
9435
9436                 /* got the maximum */
9437                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9438                                     0.0, line_width_in_chars, 0);
9439         }
9440
9441         return TRUE;
9442 }
9443
9444 typedef struct {
9445         gchar                   *header;
9446         gchar                   *entry;
9447         ComposePrefType         type;
9448         gboolean                entry_marked;
9449 } HeaderEntryState;
9450
9451 static void account_activated(GtkComboBox *optmenu, gpointer data)
9452 {
9453         Compose *compose = (Compose *)data;
9454
9455         PrefsAccount *ac;
9456         gchar *folderidentifier;
9457         gint account_id = 0;
9458         GtkTreeModel *menu;
9459         GtkTreeIter iter;
9460         GSList *list, *saved_list = NULL;
9461         HeaderEntryState *state;
9462         GtkRcStyle *style = NULL;
9463 #if !GTK_CHECK_VERSION(3, 0, 0)
9464         static GdkColor yellow;
9465         static gboolean color_set = FALSE;
9466 #else
9467         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9468 #endif
9469
9470         /* Get ID of active account in the combo box */
9471         menu = gtk_combo_box_get_model(optmenu);
9472         gtk_combo_box_get_active_iter(optmenu, &iter);
9473         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9474
9475         ac = account_find_from_id(account_id);
9476         cm_return_if_fail(ac != NULL);
9477
9478         if (ac != compose->account) {
9479                 compose_select_account(compose, ac, FALSE);
9480
9481                 for (list = compose->header_list; list; list = list->next) {
9482                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9483                         
9484                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9485                                 compose_destroy_headerentry(compose, hentry);
9486                                 continue;
9487                         }
9488                         
9489                         state = g_malloc0(sizeof(HeaderEntryState));
9490                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9491                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9492                         state->entry = gtk_editable_get_chars(
9493                                         GTK_EDITABLE(hentry->entry), 0, -1);
9494                         state->type = hentry->type;
9495                                 
9496 #if !GTK_CHECK_VERSION(3, 0, 0)
9497                         if (!color_set) {
9498                                 gdk_color_parse("#f5f6be", &yellow);
9499                                 color_set = gdk_colormap_alloc_color(
9500                                                         gdk_colormap_get_system(),
9501                                                         &yellow, FALSE, TRUE);
9502                         }
9503 #endif
9504                                 
9505                         style = gtk_widget_get_modifier_style(hentry->entry);
9506                         state->entry_marked = gdk_color_equal(&yellow,
9507                                                 &style->base[GTK_STATE_NORMAL]);
9508
9509                         saved_list = g_slist_append(saved_list, state);
9510                         compose_destroy_headerentry(compose, hentry);
9511                 }
9512
9513                 compose->header_last = NULL;
9514                 g_slist_free(compose->header_list);
9515                 compose->header_list = NULL;
9516                 compose->header_nextrow = 1;
9517                 compose_create_header_entry(compose);
9518                 
9519                 if (ac->set_autocc && ac->auto_cc)
9520                         compose_entry_append(compose, ac->auto_cc,
9521                                                 COMPOSE_CC, PREF_ACCOUNT);
9522
9523                 if (ac->set_autobcc && ac->auto_bcc) 
9524                         compose_entry_append(compose, ac->auto_bcc,
9525                                                 COMPOSE_BCC, PREF_ACCOUNT);
9526         
9527                 if (ac->set_autoreplyto && ac->auto_replyto)
9528                         compose_entry_append(compose, ac->auto_replyto,
9529                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9530                 
9531                 for (list = saved_list; list; list = list->next) {
9532                         state = (HeaderEntryState *) list->data;
9533                         
9534                         compose_add_header_entry(compose, state->header,
9535                                                 state->entry, state->type);
9536                         if (state->entry_marked)
9537                                 compose_entry_mark_default_to(compose, state->entry);
9538                         
9539                         g_free(state->header);  
9540                         g_free(state->entry);
9541                         g_free(state);
9542                 }
9543                 g_slist_free(saved_list);
9544                 
9545                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9546                                         (ac->protocol == A_NNTP) ? 
9547                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9548         }
9549
9550         /* Set message save folder */
9551         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9552                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9553         }
9554         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9555                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9556                            
9557         compose_set_save_to(compose, NULL);
9558         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9559                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9560                                   (compose->account, F_OUTBOX));
9561                 compose_set_save_to(compose, folderidentifier);
9562                 g_free(folderidentifier);
9563         }
9564 }
9565
9566 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9567                             GtkTreeViewColumn *column, Compose *compose)
9568 {
9569         compose_attach_property(NULL, compose);
9570 }
9571
9572 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9573                                       gpointer data)
9574 {
9575         Compose *compose = (Compose *)data;
9576         GtkTreeSelection *attach_selection;
9577         gint attach_nr_selected;
9578         
9579         if (!event) return FALSE;
9580
9581         if (event->button == 3) {
9582                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9583                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9584                         
9585                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9586                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9587                         
9588                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9589                                NULL, NULL, event->button, event->time);
9590                 return TRUE;                           
9591         }
9592
9593         return FALSE;
9594 }
9595
9596 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9597                                    gpointer data)
9598 {
9599         Compose *compose = (Compose *)data;
9600
9601         if (!event) return FALSE;
9602
9603         switch (event->keyval) {
9604         case GDK_KEY_Delete:
9605                 compose_attach_remove_selected(NULL, compose);
9606                 break;
9607         }
9608         return FALSE;
9609 }
9610
9611 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9612 {
9613         toolbar_comp_set_sensitive(compose, allow);
9614         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9615         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9616 #if USE_ENCHANT
9617         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9618 #endif  
9619         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9620         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9621         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9622         
9623         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9624
9625 }
9626
9627 static void compose_send_cb(GtkAction *action, gpointer data)
9628 {
9629         Compose *compose = (Compose *)data;
9630
9631         if (prefs_common.work_offline && 
9632             !inc_offline_should_override(TRUE,
9633                 _("Claws Mail needs network access in order "
9634                   "to send this email.")))
9635                 return;
9636         
9637         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9638                 g_source_remove(compose->draft_timeout_tag);
9639                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
9640         }
9641
9642         compose_send(compose);
9643 }
9644
9645 static void compose_send_later_cb(GtkAction *action, gpointer data)
9646 {
9647         Compose *compose = (Compose *)data;
9648         gint val;
9649
9650         inc_lock();
9651         compose_allow_user_actions(compose, FALSE);
9652         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9653         compose_allow_user_actions(compose, TRUE);
9654         inc_unlock();
9655
9656         if (!val) {
9657                 compose_close(compose);
9658         } else if (val == -1) {
9659                 alertpanel_error(_("Could not queue message."));
9660         } else if (val == -2) {
9661                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9662         } else if (val == -3) {
9663                 if (privacy_peek_error())
9664                 alertpanel_error(_("Could not queue message for sending:\n\n"
9665                                    "Signature failed: %s"), privacy_get_error());
9666         } else if (val == -4) {
9667                 alertpanel_error(_("Could not queue message for sending:\n\n"
9668                                    "Charset conversion failed."));
9669         } else if (val == -5) {
9670                 alertpanel_error(_("Could not queue message for sending:\n\n"
9671                                    "Couldn't get recipient encryption key."));
9672         } else if (val == -6) {
9673                 /* silent error */
9674         }
9675         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9676 }
9677
9678 #define DRAFTED_AT_EXIT "drafted_at_exit"
9679 static void compose_register_draft(MsgInfo *info)
9680 {
9681         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9682                                       DRAFTED_AT_EXIT, NULL);
9683         FILE *fp = g_fopen(filepath, "ab");
9684         
9685         if (fp) {
9686                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9687                                 info->msgnum);
9688                 fclose(fp);
9689         }
9690                 
9691         g_free(filepath);       
9692 }
9693
9694 gboolean compose_draft (gpointer data, guint action) 
9695 {
9696         Compose *compose = (Compose *)data;
9697         FolderItem *draft;
9698         gchar *tmp;
9699         gchar *sheaders;
9700         gint msgnum;
9701         MsgFlags flag = {0, 0};
9702         static gboolean lock = FALSE;
9703         MsgInfo *newmsginfo;
9704         FILE *fp;
9705         gboolean target_locked = FALSE;
9706         gboolean err = FALSE;
9707
9708         if (lock) return FALSE;
9709
9710         if (compose->sending)
9711                 return TRUE;
9712
9713         draft = account_get_special_folder(compose->account, F_DRAFT);
9714         cm_return_val_if_fail(draft != NULL, FALSE);
9715         
9716         if (!g_mutex_trylock(compose->mutex)) {
9717                 /* we don't want to lock the mutex once it's available,
9718                  * because as the only other part of compose.c locking
9719                  * it is compose_close - which means once unlocked,
9720                  * the compose struct will be freed */
9721                 debug_print("couldn't lock mutex, probably sending\n");
9722                 return FALSE;
9723         }
9724         
9725         lock = TRUE;
9726
9727         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9728                               G_DIR_SEPARATOR, compose);
9729         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9730                 FILE_OP_ERROR(tmp, "fopen");
9731                 goto warn_err;
9732         }
9733
9734         /* chmod for security */
9735         if (change_file_mode_rw(fp, tmp) < 0) {
9736                 FILE_OP_ERROR(tmp, "chmod");
9737                 g_warning("can't change file mode\n");
9738         }
9739
9740         /* Save draft infos */
9741         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9742         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9743
9744         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9745                 gchar *savefolderid;
9746
9747                 savefolderid = compose_get_save_to(compose);
9748                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9749                 g_free(savefolderid);
9750         }
9751         if (compose->return_receipt) {
9752                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9753         }
9754         if (compose->privacy_system) {
9755                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9756                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9757                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9758         }
9759
9760         /* Message-ID of message replying to */
9761         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9762                 gchar *folderid;
9763                 
9764                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9765                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9766                 g_free(folderid);
9767         }
9768         /* Message-ID of message forwarding to */
9769         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9770                 gchar *folderid;
9771                 
9772                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9773                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9774                 g_free(folderid);
9775         }
9776
9777         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9778         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9779
9780         sheaders = compose_get_manual_headers_info(compose);
9781         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9782         g_free(sheaders);
9783
9784         /* end of headers */
9785         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9786
9787         if (err) {
9788                 fclose(fp);
9789                 goto warn_err;
9790         }
9791
9792         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9793                 fclose(fp);
9794                 goto warn_err;
9795         }
9796         if (fclose(fp) == EOF) {
9797                 goto warn_err;
9798         }
9799         
9800         flag.perm_flags = MSG_NEW|MSG_UNREAD;
9801         if (compose->targetinfo) {
9802                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9803                 if (target_locked) 
9804                         flag.perm_flags |= MSG_LOCKED;
9805         }
9806         flag.tmp_flags = MSG_DRAFT;
9807
9808         folder_item_scan(draft);
9809         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9810                 MsgInfo *tmpinfo = NULL;
9811                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9812                 if (compose->msgid) {
9813                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9814                 }
9815                 if (tmpinfo) {
9816                         msgnum = tmpinfo->msgnum;
9817                         procmsg_msginfo_free(tmpinfo);
9818                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9819                 } else {
9820                         debug_print("didn't get draft msgnum after scanning\n");
9821                 }
9822         } else {
9823                 debug_print("got draft msgnum %d from adding\n", msgnum);
9824         }
9825         if (msgnum < 0) {
9826 warn_err:
9827                 claws_unlink(tmp);
9828                 g_free(tmp);
9829                 if (action != COMPOSE_AUTO_SAVE) {
9830                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9831                                 alertpanel_error(_("Could not save draft."));
9832                         else {
9833                                 AlertValue val;
9834                                 gtkut_window_popup(compose->window);
9835                                 val = alertpanel_full(_("Could not save draft"),
9836                                         _("Could not save draft.\n"
9837                                         "Do you want to cancel exit or discard this email?"),
9838                                           _("_Cancel exit"), _("_Discard email"), NULL,
9839                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9840                                 if (val == G_ALERTALTERNATE) {
9841                                         lock = FALSE;
9842                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9843                                         compose_close(compose);
9844                                         return TRUE;
9845                                 } else {
9846                                         lock = FALSE;
9847                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9848                                         return FALSE;
9849                                 }
9850                         }
9851                 }
9852                 goto unlock;
9853         }
9854         g_free(tmp);
9855
9856         if (compose->mode == COMPOSE_REEDIT) {
9857                 compose_remove_reedit_target(compose, TRUE);
9858         }
9859
9860         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9861
9862         if (newmsginfo) {
9863                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9864                 if (target_locked)
9865                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
9866                 else
9867                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
9868                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9869                         procmsg_msginfo_set_flags(newmsginfo, 0,
9870                                                   MSG_HAS_ATTACHMENT);
9871
9872                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9873                         compose_register_draft(newmsginfo);
9874                 }
9875                 procmsg_msginfo_free(newmsginfo);
9876         }
9877         
9878         folder_item_scan(draft);
9879         
9880         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9881                 lock = FALSE;
9882                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9883                 compose_close(compose);
9884                 return TRUE;
9885         } else {
9886                 struct stat s;
9887                 gchar *path;
9888
9889                 path = folder_item_fetch_msg(draft, msgnum);
9890                 if (path == NULL) {
9891                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9892                         goto unlock;
9893                 }
9894                 if (g_stat(path, &s) < 0) {
9895                         FILE_OP_ERROR(path, "stat");
9896                         g_free(path);
9897                         goto unlock;
9898                 }
9899                 g_free(path);
9900
9901                 procmsg_msginfo_free(compose->targetinfo);
9902                 compose->targetinfo = procmsg_msginfo_new();
9903                 compose->targetinfo->msgnum = msgnum;
9904                 compose->targetinfo->size = (goffset)s.st_size;
9905                 compose->targetinfo->mtime = s.st_mtime;
9906                 compose->targetinfo->folder = draft;
9907                 if (target_locked)
9908                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9909                 compose->mode = COMPOSE_REEDIT;
9910                 
9911                 if (action == COMPOSE_AUTO_SAVE) {
9912                         compose->autosaved_draft = compose->targetinfo;
9913                 }
9914                 compose->modified = FALSE;
9915                 compose_set_title(compose);
9916         }
9917 unlock:
9918         lock = FALSE;
9919         g_mutex_unlock(compose->mutex);
9920         return TRUE;
9921 }
9922
9923 void compose_clear_exit_drafts(void)
9924 {
9925         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9926                                       DRAFTED_AT_EXIT, NULL);
9927         if (is_file_exist(filepath))
9928                 claws_unlink(filepath);
9929         
9930         g_free(filepath);
9931 }
9932
9933 void compose_reopen_exit_drafts(void)
9934 {
9935         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9936                                       DRAFTED_AT_EXIT, NULL);
9937         FILE *fp = g_fopen(filepath, "rb");
9938         gchar buf[1024];
9939         
9940         if (fp) {
9941                 while (fgets(buf, sizeof(buf), fp)) {
9942                         gchar **parts = g_strsplit(buf, "\t", 2);
9943                         const gchar *folder = parts[0];
9944                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9945                         
9946                         if (folder && *folder && msgnum > -1) {
9947                                 FolderItem *item = folder_find_item_from_identifier(folder);
9948                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9949                                 if (info)
9950                                         compose_reedit(info, FALSE);
9951                         }
9952                         g_strfreev(parts);
9953                 }       
9954                 fclose(fp);
9955         }       
9956         g_free(filepath);
9957         compose_clear_exit_drafts();
9958 }
9959
9960 static void compose_save_cb(GtkAction *action, gpointer data)
9961 {
9962         Compose *compose = (Compose *)data;
9963         compose_draft(compose, COMPOSE_KEEP_EDITING);
9964         compose->rmode = COMPOSE_REEDIT;
9965 }
9966
9967 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9968 {
9969         if (compose && file_list) {
9970                 GList *tmp;
9971
9972                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9973                         gchar *file = (gchar *) tmp->data;
9974                         gchar *utf8_filename = conv_filename_to_utf8(file);
9975                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9976                         compose_changed_cb(NULL, compose);
9977                         if (free_data) {
9978                         g_free(file);
9979                                 tmp->data = NULL;
9980                         }
9981                         g_free(utf8_filename);
9982                 }
9983         }
9984 }
9985
9986 static void compose_attach_cb(GtkAction *action, gpointer data)
9987 {
9988         Compose *compose = (Compose *)data;
9989         GList *file_list;
9990
9991         if (compose->redirect_filename != NULL)
9992                 return;
9993
9994         /* Set focus_window properly, in case we were called via popup menu,
9995          * which unsets it (via focus_out_event callback on compose window). */
9996         manage_window_focus_in(compose->window, NULL, NULL);
9997
9998         file_list = filesel_select_multiple_files_open(_("Select file"));
9999
10000         if (file_list) {
10001                 compose_attach_from_list(compose, file_list, TRUE);
10002                 g_list_free(file_list);
10003         }
10004 }
10005
10006 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10007 {
10008         Compose *compose = (Compose *)data;
10009         GList *file_list;
10010         gint files_inserted = 0;
10011
10012         file_list = filesel_select_multiple_files_open(_("Select file"));
10013
10014         if (file_list) {
10015                 GList *tmp;
10016
10017                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10018                         gchar *file = (gchar *) tmp->data;
10019                         gchar *filedup = g_strdup(file);
10020                         gchar *shortfile = g_path_get_basename(filedup);
10021                         ComposeInsertResult res;
10022                         /* insert the file if the file is short or if the user confirmed that
10023                            he/she wants to insert the large file */
10024                         res = compose_insert_file(compose, file);
10025                         if (res == COMPOSE_INSERT_READ_ERROR) {
10026                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10027                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10028                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10029                                                         "for the current encoding, insertion may be incorrect."),
10030                                                         shortfile);
10031                         } else if (res == COMPOSE_INSERT_SUCCESS)
10032                                 files_inserted++;
10033
10034                         g_free(shortfile);
10035                         g_free(filedup);
10036                         g_free(file);
10037                 }
10038                 g_list_free(file_list);
10039         }
10040
10041 #ifdef USE_ENCHANT      
10042         if (files_inserted > 0 && compose->gtkaspell && 
10043             compose->gtkaspell->check_while_typing)
10044                 gtkaspell_highlight_all(compose->gtkaspell);
10045 #endif
10046 }
10047
10048 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10049 {
10050         Compose *compose = (Compose *)data;
10051
10052         compose_insert_sig(compose, FALSE);
10053 }
10054
10055 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10056 {
10057         Compose *compose = (Compose *)data;
10058
10059         compose_insert_sig(compose, TRUE);
10060 }
10061
10062 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10063                               gpointer data)
10064 {
10065         gint x, y;
10066         Compose *compose = (Compose *)data;
10067
10068         gtkut_widget_get_uposition(widget, &x, &y);
10069         if (!compose->batch) {
10070                 prefs_common.compose_x = x;
10071                 prefs_common.compose_y = y;
10072         }
10073         if (compose->sending || compose->updating)
10074                 return TRUE;
10075         compose_close_cb(NULL, compose);
10076         return TRUE;
10077 }
10078
10079 void compose_close_toolbar(Compose *compose)
10080 {
10081         compose_close_cb(NULL, compose);
10082 }
10083
10084 static gboolean compose_can_autosave(Compose *compose)
10085 {
10086         if (compose->privacy_system && compose->use_encryption)
10087                 return prefs_common.autosave && prefs_common.autosave_encrypted;
10088         else
10089                 return prefs_common.autosave;
10090 }
10091
10092 static void compose_close_cb(GtkAction *action, gpointer data)
10093 {
10094         Compose *compose = (Compose *)data;
10095         AlertValue val;
10096
10097 #ifdef G_OS_UNIX
10098         if (compose->exteditor_tag != -1) {
10099                 if (!compose_ext_editor_kill(compose))
10100                         return;
10101         }
10102 #endif
10103
10104         if (compose->modified) {
10105                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10106                 if (!g_mutex_trylock(compose->mutex)) {
10107                         /* we don't want to lock the mutex once it's available,
10108                          * because as the only other part of compose.c locking
10109                          * it is compose_close - which means once unlocked,
10110                          * the compose struct will be freed */
10111                         debug_print("couldn't lock mutex, probably sending\n");
10112                         return;
10113                 }
10114                 if (!reedit) {
10115                         val = alertpanel(_("Discard message"),
10116                                  _("This message has been modified. Discard it?"),
10117                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10118                 } else {
10119                         val = alertpanel(_("Save changes"),
10120                                  _("This message has been modified. Save the latest changes?"),
10121                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10122                 }
10123                 g_mutex_unlock(compose->mutex);
10124                 switch (val) {
10125                 case G_ALERTDEFAULT:
10126                         if (compose_can_autosave(compose) && !reedit)
10127                                 compose_remove_draft(compose);
10128                         break;
10129                 case G_ALERTALTERNATE:
10130                         compose_draft(data, COMPOSE_QUIT_EDITING);
10131                         return;
10132                 default:
10133                         return;
10134                 }
10135         }
10136
10137         compose_close(compose);
10138 }
10139
10140 static void compose_print_cb(GtkAction *action, gpointer data)
10141 {
10142         Compose *compose = (Compose *) data;
10143
10144         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10145         if (compose->targetinfo)
10146                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10147 }
10148
10149 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10150 {
10151         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10152         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10153         Compose *compose = (Compose *) data;
10154
10155         if (active)
10156                 compose->out_encoding = (CharSet)value;
10157 }
10158
10159 static void compose_address_cb(GtkAction *action, gpointer data)
10160 {
10161         Compose *compose = (Compose *)data;
10162
10163 #ifndef USE_NEW_ADDRBOOK
10164         addressbook_open(compose);
10165 #else
10166         GError* error = NULL;
10167         addressbook_connect_signals(compose);
10168         addressbook_dbus_open(TRUE, &error);
10169         if (error) {
10170                 g_warning("%s", error->message);
10171                 g_error_free(error);
10172         }
10173 #endif
10174 }
10175
10176 static void about_show_cb(GtkAction *action, gpointer data)
10177 {
10178         about_show();
10179 }
10180
10181 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10182 {
10183         Compose *compose = (Compose *)data;
10184         Template *tmpl;
10185         gchar *msg;
10186         AlertValue val;
10187
10188         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10189         cm_return_if_fail(tmpl != NULL);
10190
10191         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10192                               tmpl->name);
10193         val = alertpanel(_("Apply template"), msg,
10194                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10195         g_free(msg);
10196
10197         if (val == G_ALERTDEFAULT)
10198                 compose_template_apply(compose, tmpl, TRUE);
10199         else if (val == G_ALERTALTERNATE)
10200                 compose_template_apply(compose, tmpl, FALSE);
10201 }
10202
10203 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10204 {
10205         Compose *compose = (Compose *)data;
10206
10207         compose_exec_ext_editor(compose);
10208 }
10209
10210 static void compose_undo_cb(GtkAction *action, gpointer data)
10211 {
10212         Compose *compose = (Compose *)data;
10213         gboolean prev_autowrap = compose->autowrap;
10214
10215         compose->autowrap = FALSE;
10216         undo_undo(compose->undostruct);
10217         compose->autowrap = prev_autowrap;
10218 }
10219
10220 static void compose_redo_cb(GtkAction *action, gpointer data)
10221 {
10222         Compose *compose = (Compose *)data;
10223         gboolean prev_autowrap = compose->autowrap;
10224         
10225         compose->autowrap = FALSE;
10226         undo_redo(compose->undostruct);
10227         compose->autowrap = prev_autowrap;
10228 }
10229
10230 static void entry_cut_clipboard(GtkWidget *entry)
10231 {
10232         if (GTK_IS_EDITABLE(entry))
10233                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10234         else if (GTK_IS_TEXT_VIEW(entry))
10235                 gtk_text_buffer_cut_clipboard(
10236                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10237                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10238                         TRUE);
10239 }
10240
10241 static void entry_copy_clipboard(GtkWidget *entry)
10242 {
10243         if (GTK_IS_EDITABLE(entry))
10244                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10245         else if (GTK_IS_TEXT_VIEW(entry))
10246                 gtk_text_buffer_copy_clipboard(
10247                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10248                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10249 }
10250
10251 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10252                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10253 {
10254         if (GTK_IS_TEXT_VIEW(entry)) {
10255                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10256                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10257                 GtkTextIter start_iter, end_iter;
10258                 gint start, end;
10259                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10260
10261                 if (contents == NULL)
10262                         return;
10263         
10264                 /* we shouldn't delete the selection when middle-click-pasting, or we
10265                  * can't mid-click-paste our own selection */
10266                 if (clip != GDK_SELECTION_PRIMARY) {
10267                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10268                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10269                 }
10270                 
10271                 if (insert_place == NULL) {
10272                         /* if insert_place isn't specified, insert at the cursor.
10273                          * used for Ctrl-V pasting */
10274                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10275                         start = gtk_text_iter_get_offset(&start_iter);
10276                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10277                 } else {
10278                         /* if insert_place is specified, paste here.
10279                          * used for mid-click-pasting */
10280                         start = gtk_text_iter_get_offset(insert_place);
10281                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10282                         if (prefs_common.primary_paste_unselects)
10283                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10284                 }
10285                 
10286                 if (!wrap) {
10287                         /* paste unwrapped: mark the paste so it's not wrapped later */
10288                         end = start + strlen(contents);
10289                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10290                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10291                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10292                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10293                         /* rewrap paragraph now (after a mid-click-paste) */
10294                         mark_start = gtk_text_buffer_get_insert(buffer);
10295                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10296                         gtk_text_iter_backward_char(&start_iter);
10297                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10298                 }
10299         } else if (GTK_IS_EDITABLE(entry))
10300                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10301
10302         compose->modified = TRUE;
10303 }
10304
10305 static void entry_allsel(GtkWidget *entry)
10306 {
10307         if (GTK_IS_EDITABLE(entry))
10308                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10309         else if (GTK_IS_TEXT_VIEW(entry)) {
10310                 GtkTextIter startiter, enditer;
10311                 GtkTextBuffer *textbuf;
10312
10313                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10314                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10315                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10316
10317                 gtk_text_buffer_move_mark_by_name(textbuf, 
10318                         "selection_bound", &startiter);
10319                 gtk_text_buffer_move_mark_by_name(textbuf, 
10320                         "insert", &enditer);
10321         }
10322 }
10323
10324 static void compose_cut_cb(GtkAction *action, gpointer data)
10325 {
10326         Compose *compose = (Compose *)data;
10327         if (compose->focused_editable 
10328 #ifndef GENERIC_UMPC
10329             && gtk_widget_has_focus(compose->focused_editable)
10330 #endif
10331             )
10332                 entry_cut_clipboard(compose->focused_editable);
10333 }
10334
10335 static void compose_copy_cb(GtkAction *action, gpointer data)
10336 {
10337         Compose *compose = (Compose *)data;
10338         if (compose->focused_editable 
10339 #ifndef GENERIC_UMPC
10340             && gtk_widget_has_focus(compose->focused_editable)
10341 #endif
10342             )
10343                 entry_copy_clipboard(compose->focused_editable);
10344 }
10345
10346 static void compose_paste_cb(GtkAction *action, gpointer data)
10347 {
10348         Compose *compose = (Compose *)data;
10349         gint prev_autowrap;
10350         GtkTextBuffer *buffer;
10351         BLOCK_WRAP();
10352         if (compose->focused_editable &&
10353 #ifndef GENERIC_UMPC
10354             gtk_widget_has_focus(compose->focused_editable)
10355 #endif
10356                 )
10357                 entry_paste_clipboard(compose, compose->focused_editable, 
10358                                 prefs_common.linewrap_pastes,
10359                                 GDK_SELECTION_CLIPBOARD, NULL);
10360         UNBLOCK_WRAP();
10361
10362 #ifdef USE_ENCHANT
10363         if (
10364 #ifndef GENERIC_UMPC
10365                 gtk_widget_has_focus(compose->text) &&
10366 #endif
10367             compose->gtkaspell && 
10368             compose->gtkaspell->check_while_typing)
10369                 gtkaspell_highlight_all(compose->gtkaspell);
10370 #endif
10371 }
10372
10373 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10374 {
10375         Compose *compose = (Compose *)data;
10376         gint wrap_quote = prefs_common.linewrap_quote;
10377         if (compose->focused_editable 
10378 #ifndef GENERIC_UMPC
10379             && gtk_widget_has_focus(compose->focused_editable)
10380 #endif
10381             ) {
10382                 /* let text_insert() (called directly or at a later time
10383                  * after the gtk_editable_paste_clipboard) know that 
10384                  * text is to be inserted as a quotation. implemented
10385                  * by using a simple refcount... */
10386                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10387                                                 G_OBJECT(compose->focused_editable),
10388                                                 "paste_as_quotation"));
10389                 g_object_set_data(G_OBJECT(compose->focused_editable),
10390                                     "paste_as_quotation",
10391                                     GINT_TO_POINTER(paste_as_quotation + 1));
10392                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10393                 entry_paste_clipboard(compose, compose->focused_editable, 
10394                                 prefs_common.linewrap_pastes,
10395                                 GDK_SELECTION_CLIPBOARD, NULL);
10396                 prefs_common.linewrap_quote = wrap_quote;
10397         }
10398 }
10399
10400 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10401 {
10402         Compose *compose = (Compose *)data;
10403         gint prev_autowrap;
10404         GtkTextBuffer *buffer;
10405         BLOCK_WRAP();
10406         if (compose->focused_editable 
10407 #ifndef GENERIC_UMPC
10408             && gtk_widget_has_focus(compose->focused_editable)
10409 #endif
10410             )
10411                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10412                         GDK_SELECTION_CLIPBOARD, NULL);
10413         UNBLOCK_WRAP();
10414
10415 #ifdef USE_ENCHANT
10416         if (
10417 #ifndef GENERIC_UMPC
10418                 gtk_widget_has_focus(compose->text) &&
10419 #endif
10420             compose->gtkaspell && 
10421             compose->gtkaspell->check_while_typing)
10422                 gtkaspell_highlight_all(compose->gtkaspell);
10423 #endif
10424 }
10425
10426 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10427 {
10428         Compose *compose = (Compose *)data;
10429         gint prev_autowrap;
10430         GtkTextBuffer *buffer;
10431         BLOCK_WRAP();
10432         if (compose->focused_editable 
10433 #ifndef GENERIC_UMPC
10434             && gtk_widget_has_focus(compose->focused_editable)
10435 #endif
10436             )
10437                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10438                         GDK_SELECTION_CLIPBOARD, NULL);
10439         UNBLOCK_WRAP();
10440
10441 #ifdef USE_ENCHANT
10442         if (
10443 #ifndef GENERIC_UMPC
10444                 gtk_widget_has_focus(compose->text) &&
10445 #endif
10446             compose->gtkaspell &&
10447             compose->gtkaspell->check_while_typing)
10448                 gtkaspell_highlight_all(compose->gtkaspell);
10449 #endif
10450 }
10451
10452 static void compose_allsel_cb(GtkAction *action, gpointer data)
10453 {
10454         Compose *compose = (Compose *)data;
10455         if (compose->focused_editable 
10456 #ifndef GENERIC_UMPC
10457             && gtk_widget_has_focus(compose->focused_editable)
10458 #endif
10459             )
10460                 entry_allsel(compose->focused_editable);
10461 }
10462
10463 static void textview_move_beginning_of_line (GtkTextView *text)
10464 {
10465         GtkTextBuffer *buffer;
10466         GtkTextMark *mark;
10467         GtkTextIter ins;
10468
10469         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10470
10471         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10472         mark = gtk_text_buffer_get_insert(buffer);
10473         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10474         gtk_text_iter_set_line_offset(&ins, 0);
10475         gtk_text_buffer_place_cursor(buffer, &ins);
10476 }
10477
10478 static void textview_move_forward_character (GtkTextView *text)
10479 {
10480         GtkTextBuffer *buffer;
10481         GtkTextMark *mark;
10482         GtkTextIter ins;
10483
10484         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10485
10486         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10487         mark = gtk_text_buffer_get_insert(buffer);
10488         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10489         if (gtk_text_iter_forward_cursor_position(&ins))
10490                 gtk_text_buffer_place_cursor(buffer, &ins);
10491 }
10492
10493 static void textview_move_backward_character (GtkTextView *text)
10494 {
10495         GtkTextBuffer *buffer;
10496         GtkTextMark *mark;
10497         GtkTextIter ins;
10498
10499         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10500
10501         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10502         mark = gtk_text_buffer_get_insert(buffer);
10503         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10504         if (gtk_text_iter_backward_cursor_position(&ins))
10505                 gtk_text_buffer_place_cursor(buffer, &ins);
10506 }
10507
10508 static void textview_move_forward_word (GtkTextView *text)
10509 {
10510         GtkTextBuffer *buffer;
10511         GtkTextMark *mark;
10512         GtkTextIter ins;
10513         gint count;
10514
10515         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10516
10517         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10518         mark = gtk_text_buffer_get_insert(buffer);
10519         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10520         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10521         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10522                 gtk_text_iter_backward_word_start(&ins);
10523                 gtk_text_buffer_place_cursor(buffer, &ins);
10524         }
10525 }
10526
10527 static void textview_move_backward_word (GtkTextView *text)
10528 {
10529         GtkTextBuffer *buffer;
10530         GtkTextMark *mark;
10531         GtkTextIter ins;
10532
10533         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10534
10535         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10536         mark = gtk_text_buffer_get_insert(buffer);
10537         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10538         if (gtk_text_iter_backward_word_starts(&ins, 1))
10539                 gtk_text_buffer_place_cursor(buffer, &ins);
10540 }
10541
10542 static void textview_move_end_of_line (GtkTextView *text)
10543 {
10544         GtkTextBuffer *buffer;
10545         GtkTextMark *mark;
10546         GtkTextIter ins;
10547
10548         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10549
10550         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10551         mark = gtk_text_buffer_get_insert(buffer);
10552         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10553         if (gtk_text_iter_forward_to_line_end(&ins))
10554                 gtk_text_buffer_place_cursor(buffer, &ins);
10555 }
10556
10557 static void textview_move_next_line (GtkTextView *text)
10558 {
10559         GtkTextBuffer *buffer;
10560         GtkTextMark *mark;
10561         GtkTextIter ins;
10562         gint offset;
10563
10564         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10565
10566         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10567         mark = gtk_text_buffer_get_insert(buffer);
10568         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10569         offset = gtk_text_iter_get_line_offset(&ins);
10570         if (gtk_text_iter_forward_line(&ins)) {
10571                 gtk_text_iter_set_line_offset(&ins, offset);
10572                 gtk_text_buffer_place_cursor(buffer, &ins);
10573         }
10574 }
10575
10576 static void textview_move_previous_line (GtkTextView *text)
10577 {
10578         GtkTextBuffer *buffer;
10579         GtkTextMark *mark;
10580         GtkTextIter ins;
10581         gint offset;
10582
10583         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10584
10585         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10586         mark = gtk_text_buffer_get_insert(buffer);
10587         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10588         offset = gtk_text_iter_get_line_offset(&ins);
10589         if (gtk_text_iter_backward_line(&ins)) {
10590                 gtk_text_iter_set_line_offset(&ins, offset);
10591                 gtk_text_buffer_place_cursor(buffer, &ins);
10592         }
10593 }
10594
10595 static void textview_delete_forward_character (GtkTextView *text)
10596 {
10597         GtkTextBuffer *buffer;
10598         GtkTextMark *mark;
10599         GtkTextIter ins, end_iter;
10600
10601         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10602
10603         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10604         mark = gtk_text_buffer_get_insert(buffer);
10605         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10606         end_iter = ins;
10607         if (gtk_text_iter_forward_char(&end_iter)) {
10608                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10609         }
10610 }
10611
10612 static void textview_delete_backward_character (GtkTextView *text)
10613 {
10614         GtkTextBuffer *buffer;
10615         GtkTextMark *mark;
10616         GtkTextIter ins, end_iter;
10617
10618         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10619
10620         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10621         mark = gtk_text_buffer_get_insert(buffer);
10622         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10623         end_iter = ins;
10624         if (gtk_text_iter_backward_char(&end_iter)) {
10625                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10626         }
10627 }
10628
10629 static void textview_delete_forward_word (GtkTextView *text)
10630 {
10631         GtkTextBuffer *buffer;
10632         GtkTextMark *mark;
10633         GtkTextIter ins, end_iter;
10634
10635         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10636
10637         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10638         mark = gtk_text_buffer_get_insert(buffer);
10639         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10640         end_iter = ins;
10641         if (gtk_text_iter_forward_word_end(&end_iter)) {
10642                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10643         }
10644 }
10645
10646 static void textview_delete_backward_word (GtkTextView *text)
10647 {
10648         GtkTextBuffer *buffer;
10649         GtkTextMark *mark;
10650         GtkTextIter ins, end_iter;
10651
10652         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10653
10654         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10655         mark = gtk_text_buffer_get_insert(buffer);
10656         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10657         end_iter = ins;
10658         if (gtk_text_iter_backward_word_start(&end_iter)) {
10659                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10660         }
10661 }
10662
10663 static void textview_delete_line (GtkTextView *text)
10664 {
10665         GtkTextBuffer *buffer;
10666         GtkTextMark *mark;
10667         GtkTextIter ins, start_iter, end_iter;
10668
10669         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10670
10671         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10672         mark = gtk_text_buffer_get_insert(buffer);
10673         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10674
10675         start_iter = ins;
10676         gtk_text_iter_set_line_offset(&start_iter, 0);
10677
10678         end_iter = ins;
10679         if (gtk_text_iter_ends_line(&end_iter)){
10680                 if (!gtk_text_iter_forward_char(&end_iter))
10681                         gtk_text_iter_backward_char(&start_iter);
10682         }
10683         else 
10684                 gtk_text_iter_forward_to_line_end(&end_iter);
10685         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10686 }
10687
10688 static void textview_delete_to_line_end (GtkTextView *text)
10689 {
10690         GtkTextBuffer *buffer;
10691         GtkTextMark *mark;
10692         GtkTextIter ins, end_iter;
10693
10694         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10695
10696         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10697         mark = gtk_text_buffer_get_insert(buffer);
10698         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10699         end_iter = ins;
10700         if (gtk_text_iter_ends_line(&end_iter))
10701                 gtk_text_iter_forward_char(&end_iter);
10702         else
10703                 gtk_text_iter_forward_to_line_end(&end_iter);
10704         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10705 }
10706
10707 #define DO_ACTION(name, act) {                                          \
10708         if(!strcmp(name, a_name)) {                                     \
10709                 return act;                                             \
10710         }                                                               \
10711 }
10712 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10713 {
10714         const gchar *a_name = gtk_action_get_name(action);
10715         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10716         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10717         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10718         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10719         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10720         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10721         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10722         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10723         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10724         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10725         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10726         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10727         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10728         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10729         return -1;
10730 }
10731
10732 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10733 {
10734         Compose *compose = (Compose *)data;
10735         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10736         ComposeCallAdvancedAction action = -1;
10737         
10738         action = compose_call_advanced_action_from_path(gaction);
10739
10740         static struct {
10741                 void (*do_action) (GtkTextView *text);
10742         } action_table[] = {
10743                 {textview_move_beginning_of_line},
10744                 {textview_move_forward_character},
10745                 {textview_move_backward_character},
10746                 {textview_move_forward_word},
10747                 {textview_move_backward_word},
10748                 {textview_move_end_of_line},
10749                 {textview_move_next_line},
10750                 {textview_move_previous_line},
10751                 {textview_delete_forward_character},
10752                 {textview_delete_backward_character},
10753                 {textview_delete_forward_word},
10754                 {textview_delete_backward_word},
10755                 {textview_delete_line},
10756                 {textview_delete_to_line_end}
10757         };
10758
10759         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10760
10761         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10762             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10763                 if (action_table[action].do_action)
10764                         action_table[action].do_action(text);
10765                 else
10766                         g_warning("Not implemented yet.");
10767         }
10768 }
10769
10770 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10771 {
10772         GtkAllocation allocation;
10773         GtkWidget *parent;
10774         gchar *str = NULL;
10775         
10776         if (GTK_IS_EDITABLE(widget)) {
10777                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10778                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10779                         strlen(str));
10780                 g_free(str);
10781                 if ((parent = gtk_widget_get_parent(widget))
10782                  && (parent = gtk_widget_get_parent(parent))
10783                  && (parent = gtk_widget_get_parent(parent))) {
10784                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
10785                                 gtk_widget_get_allocation(widget, &allocation);
10786                                 gint y = allocation.y;
10787                                 gint height = allocation.height;
10788                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10789                                         (GTK_SCROLLED_WINDOW(parent));
10790
10791                                 gfloat value = gtk_adjustment_get_value(shown);
10792                                 gfloat upper = gtk_adjustment_get_upper(shown);
10793                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
10794                                 if (y < (int)value) {
10795                                         gtk_adjustment_set_value(shown, y - 1);
10796                                 }
10797                                 if ((y + height) > ((int)value + (int)page_size)) {
10798                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
10799                                                 gtk_adjustment_set_value(shown, 
10800                                                         y + height - (int)page_size - 1);
10801                                         } else {
10802                                                 gtk_adjustment_set_value(shown, 
10803                                                         (int)upper - (int)page_size - 1);
10804                                         }
10805                                 }
10806                         }
10807                 }
10808         }
10809
10810         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10811                 compose->focused_editable = widget;
10812         
10813 #ifdef GENERIC_UMPC
10814         if (GTK_IS_TEXT_VIEW(widget) 
10815             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10816                 g_object_ref(compose->notebook);
10817                 g_object_ref(compose->edit_vbox);
10818                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10819                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10820                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10821                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10822                 g_object_unref(compose->notebook);
10823                 g_object_unref(compose->edit_vbox);
10824                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10825                                         G_CALLBACK(compose_grab_focus_cb),
10826                                         compose);
10827                 gtk_widget_grab_focus(widget);
10828                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10829                                         G_CALLBACK(compose_grab_focus_cb),
10830                                         compose);
10831         } else if (!GTK_IS_TEXT_VIEW(widget) 
10832                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10833                 g_object_ref(compose->notebook);
10834                 g_object_ref(compose->edit_vbox);
10835                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10836                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10837                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10838                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10839                 g_object_unref(compose->notebook);
10840                 g_object_unref(compose->edit_vbox);
10841                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10842                                         G_CALLBACK(compose_grab_focus_cb),
10843                                         compose);
10844                 gtk_widget_grab_focus(widget);
10845                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10846                                         G_CALLBACK(compose_grab_focus_cb),
10847                                         compose);
10848         }
10849 #endif
10850 }
10851
10852 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10853 {
10854         compose->modified = TRUE;
10855 //      compose_beautify_paragraph(compose, NULL, TRUE);
10856 #ifndef GENERIC_UMPC
10857         compose_set_title(compose);
10858 #endif
10859 }
10860
10861 static void compose_wrap_cb(GtkAction *action, gpointer data)
10862 {
10863         Compose *compose = (Compose *)data;
10864         compose_beautify_paragraph(compose, NULL, TRUE);
10865 }
10866
10867 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10868 {
10869         Compose *compose = (Compose *)data;
10870         compose_wrap_all_full(compose, TRUE);
10871 }
10872
10873 static void compose_find_cb(GtkAction *action, gpointer data)
10874 {
10875         Compose *compose = (Compose *)data;
10876
10877         message_search_compose(compose);
10878 }
10879
10880 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10881                                          gpointer        data)
10882 {
10883         Compose *compose = (Compose *)data;
10884         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10885         if (compose->autowrap)
10886                 compose_wrap_all_full(compose, TRUE);
10887         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10888 }
10889
10890 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10891                                          gpointer        data)
10892 {
10893         Compose *compose = (Compose *)data;
10894         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10895 }
10896
10897 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10898 {
10899         Compose *compose = (Compose *)data;
10900
10901         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10902 }
10903
10904 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10905 {
10906         Compose *compose = (Compose *)data;
10907
10908         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10909 }
10910
10911 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10912 {
10913         g_free(compose->privacy_system);
10914
10915         compose->privacy_system = g_strdup(account->default_privacy_system);
10916         compose_update_privacy_system_menu_item(compose, warn);
10917 }
10918
10919 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10920 {
10921         Compose *compose = (Compose *)data;
10922
10923         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10924                 gtk_widget_show(compose->ruler_hbox);
10925                 prefs_common.show_ruler = TRUE;
10926         } else {
10927                 gtk_widget_hide(compose->ruler_hbox);
10928                 gtk_widget_queue_resize(compose->edit_vbox);
10929                 prefs_common.show_ruler = FALSE;
10930         }
10931 }
10932
10933 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10934                                              GdkDragContext     *context,
10935                                              gint                x,
10936                                              gint                y,
10937                                              GtkSelectionData   *data,
10938                                              guint               info,
10939                                              guint               time,
10940                                              gpointer            user_data)
10941 {
10942         Compose *compose = (Compose *)user_data;
10943         GList *list, *tmp;
10944         GdkAtom type;
10945
10946         type = gtk_selection_data_get_data_type(data);
10947         if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10948 #ifdef G_OS_WIN32
10949          || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10950 #endif
10951            ) && gtk_drag_get_source_widget(context) != 
10952                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10953                 list = uri_list_extract_filenames(
10954                         (const gchar *)gtk_selection_data_get_data(data));
10955                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10956                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10957                         compose_attach_append
10958                                 (compose, (const gchar *)tmp->data,
10959                                  utf8_filename, NULL, NULL);
10960                         g_free(utf8_filename);
10961                 }
10962                 if (list) compose_changed_cb(NULL, compose);
10963                 list_free_strings(list);
10964                 g_list_free(list);
10965         } else if (gtk_drag_get_source_widget(context) 
10966                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10967                 /* comes from our summaryview */
10968                 SummaryView * summaryview = NULL;
10969                 GSList * list = NULL, *cur = NULL;
10970                 
10971                 if (mainwindow_get_mainwindow())
10972                         summaryview = mainwindow_get_mainwindow()->summaryview;
10973                 
10974                 if (summaryview)
10975                         list = summary_get_selected_msg_list(summaryview);
10976                 
10977                 for (cur = list; cur; cur = cur->next) {
10978                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10979                         gchar *file = NULL;
10980                         if (msginfo)
10981                                 file = procmsg_get_message_file_full(msginfo, 
10982                                         TRUE, TRUE);
10983                         if (file) {
10984                                 compose_attach_append(compose, (const gchar *)file, 
10985                                         (const gchar *)file, "message/rfc822", NULL);
10986                                 g_free(file);
10987                         }
10988                 }
10989                 g_slist_free(list);
10990         }
10991 }
10992
10993 static gboolean compose_drag_drop(GtkWidget *widget,
10994                                   GdkDragContext *drag_context,
10995                                   gint x, gint y,
10996                                   guint time, gpointer user_data)
10997 {
10998         /* not handling this signal makes compose_insert_drag_received_cb
10999          * called twice */
11000         return TRUE;                                     
11001 }
11002
11003 static gboolean completion_set_focus_to_subject
11004                                         (GtkWidget    *widget,
11005                                          GdkEventKey  *event,
11006                                          Compose      *compose)
11007 {
11008         cm_return_val_if_fail(compose != NULL, FALSE);
11009
11010         /* make backtab move to subject field */
11011         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11012                 gtk_widget_grab_focus(compose->subject_entry);
11013                 return TRUE;
11014         }
11015         return FALSE;
11016 }
11017
11018 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11019                                              GdkDragContext     *drag_context,
11020                                              gint                x,
11021                                              gint                y,
11022                                              GtkSelectionData   *data,
11023                                              guint               info,
11024                                              guint               time,
11025                                              gpointer            user_data)
11026 {
11027         Compose *compose = (Compose *)user_data;
11028         GList *list, *tmp;
11029         GdkAtom type;
11030
11031         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11032          * does not work */
11033         type = gtk_selection_data_get_data_type(data);
11034 #ifndef G_OS_WIN32
11035         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11036 #else
11037         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
11038 #endif
11039                 AlertValue val = G_ALERTDEFAULT;
11040                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11041
11042                 list = uri_list_extract_filenames(ddata);
11043                 if (list == NULL && strstr(ddata, "://")) {
11044                         /* Assume a list of no files, and data has ://, is a remote link */
11045                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11046                         gchar *tmpfile = get_tmp_file();
11047                         str_write_to_file(tmpdata, tmpfile);
11048                         g_free(tmpdata);  
11049                         compose_insert_file(compose, tmpfile);
11050                         claws_unlink(tmpfile);
11051                         g_free(tmpfile);
11052                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11053                         compose_beautify_paragraph(compose, NULL, TRUE);
11054                         return;
11055                 }
11056                 switch (prefs_common.compose_dnd_mode) {
11057                         case COMPOSE_DND_ASK:
11058                                 val = alertpanel_full(_("Insert or attach?"),
11059                                          _("Do you want to insert the contents of the file(s) "
11060                                            "into the message body, or attach it to the email?"),
11061                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
11062                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11063                                 break;
11064                         case COMPOSE_DND_INSERT:
11065                                 val = G_ALERTALTERNATE;
11066                                 break;
11067                         case COMPOSE_DND_ATTACH:
11068                                 val = G_ALERTOTHER;
11069                                 break;
11070                         default:
11071                                 /* unexpected case */
11072                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11073                 }
11074
11075                 if (val & G_ALERTDISABLE) {
11076                         val &= ~G_ALERTDISABLE;
11077                         /* remember what action to perform by default, only if we don't click Cancel */
11078                         if (val == G_ALERTALTERNATE)
11079                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11080                         else if (val == G_ALERTOTHER)
11081                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11082                 }
11083
11084                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11085                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11086                         list_free_strings(list);
11087                         g_list_free(list);
11088                         return;
11089                 } else if (val == G_ALERTOTHER) {
11090                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11091                         list_free_strings(list);
11092                         g_list_free(list);
11093                         return;
11094                 } 
11095
11096                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11097                         compose_insert_file(compose, (const gchar *)tmp->data);
11098                 }
11099                 list_free_strings(list);
11100                 g_list_free(list);
11101                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11102                 return;
11103         } else {
11104                 return;
11105         }
11106         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11107 }
11108
11109 static void compose_header_drag_received_cb (GtkWidget          *widget,
11110                                              GdkDragContext     *drag_context,
11111                                              gint                x,
11112                                              gint                y,
11113                                              GtkSelectionData   *data,
11114                                              guint               info,
11115                                              guint               time,
11116                                              gpointer            user_data)
11117 {
11118         GtkEditable *entry = (GtkEditable *)user_data;
11119         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11120
11121         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11122          * does not work */
11123
11124         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11125                 gchar *decoded=g_new(gchar, strlen(email));
11126                 int start = 0;
11127
11128                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11129                 gtk_editable_delete_text(entry, 0, -1);
11130                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11131                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11132                 g_free(decoded);
11133                 return;
11134         }
11135         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11136 }
11137
11138 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11139 {
11140         Compose *compose = (Compose *)data;
11141
11142         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11143                 compose->return_receipt = TRUE;
11144         else
11145                 compose->return_receipt = FALSE;
11146 }
11147
11148 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11149 {
11150         Compose *compose = (Compose *)data;
11151
11152         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11153                 compose->remove_references = TRUE;
11154         else
11155                 compose->remove_references = FALSE;
11156 }
11157
11158 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11159                                         ComposeHeaderEntry *headerentry)
11160 {
11161         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11162         return FALSE;
11163 }
11164
11165 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11166                                             GdkEventKey *event,
11167                                             ComposeHeaderEntry *headerentry)
11168 {
11169         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11170             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11171             !(event->state & GDK_MODIFIER_MASK) &&
11172             (event->keyval == GDK_KEY_BackSpace) &&
11173             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11174                 gtk_container_remove
11175                         (GTK_CONTAINER(headerentry->compose->header_table),
11176                          headerentry->combo);
11177                 gtk_container_remove
11178                         (GTK_CONTAINER(headerentry->compose->header_table),
11179                          headerentry->entry);
11180                 headerentry->compose->header_list =
11181                         g_slist_remove(headerentry->compose->header_list,
11182                                        headerentry);
11183                 g_free(headerentry);
11184         } else  if (event->keyval == GDK_KEY_Tab) {
11185                 if (headerentry->compose->header_last == headerentry) {
11186                         /* Override default next focus, and give it to subject_entry
11187                          * instead of notebook tabs
11188                          */
11189                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11190                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11191                         return TRUE;
11192                 }
11193         }
11194         return FALSE;
11195 }
11196
11197 static gboolean scroll_postpone(gpointer data)
11198 {
11199         Compose *compose = (Compose *)data;
11200
11201         if (compose->batch)
11202                 return FALSE;
11203
11204         GTK_EVENTS_FLUSH();
11205         compose_show_first_last_header(compose, FALSE);
11206         return FALSE;
11207 }
11208
11209 static void compose_headerentry_changed_cb(GtkWidget *entry,
11210                                     ComposeHeaderEntry *headerentry)
11211 {
11212         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11213                 compose_create_header_entry(headerentry->compose);
11214                 g_signal_handlers_disconnect_matched
11215                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11216                          0, 0, NULL, NULL, headerentry);
11217
11218                 if (!headerentry->compose->batch)
11219                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11220         }
11221 }
11222
11223 static gboolean compose_defer_auto_save_draft(Compose *compose)
11224 {
11225         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11226         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11227         return FALSE;
11228 }
11229
11230 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11231 {
11232         GtkAdjustment *vadj;
11233
11234         cm_return_if_fail(compose);
11235
11236         if(compose->batch)
11237                 return;
11238
11239         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11240         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11241         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11242                                 gtk_widget_get_parent(compose->header_table)));
11243         gtk_adjustment_set_value(vadj, (show_first ?
11244                                 gtk_adjustment_get_lower(vadj) :
11245                                 (gtk_adjustment_get_upper(vadj) -
11246                                 gtk_adjustment_get_page_size(vadj))));
11247         gtk_adjustment_changed(vadj);
11248 }
11249
11250 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11251                           const gchar *text, gint len, Compose *compose)
11252 {
11253         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11254                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11255         GtkTextMark *mark;
11256
11257         cm_return_if_fail(text != NULL);
11258
11259         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11260                                         G_CALLBACK(text_inserted),
11261                                         compose);
11262         if (paste_as_quotation) {
11263                 gchar *new_text;
11264                 const gchar *qmark;
11265                 guint pos = 0;
11266                 GtkTextIter start_iter;
11267
11268                 if (len < 0)
11269                         len = strlen(text);
11270
11271                 new_text = g_strndup(text, len);
11272
11273                 qmark = compose_quote_char_from_context(compose);
11274
11275                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11276                 gtk_text_buffer_place_cursor(buffer, iter);
11277
11278                 pos = gtk_text_iter_get_offset(iter);
11279
11280                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11281                                                   _("Quote format error at line %d."));
11282                 quote_fmt_reset_vartable();
11283                 g_free(new_text);
11284                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11285                                   GINT_TO_POINTER(paste_as_quotation - 1));
11286                                   
11287                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11288                 gtk_text_buffer_place_cursor(buffer, iter);
11289                 gtk_text_buffer_delete_mark(buffer, mark);
11290
11291                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11292                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11293                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11294                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11295                 gtk_text_buffer_delete_mark(buffer, mark);
11296         } else {
11297                 if (strcmp(text, "\n") || compose->automatic_break
11298                 || gtk_text_iter_starts_line(iter)) {
11299                         GtkTextIter before_ins;
11300                         gtk_text_buffer_insert(buffer, iter, text, len);
11301                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11302                                 before_ins = *iter; 
11303                                 gtk_text_iter_backward_chars(&before_ins, len);
11304                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11305                         }
11306                 } else {
11307                         /* check if the preceding is just whitespace or quote */
11308                         GtkTextIter start_line;
11309                         gchar *tmp = NULL, *quote = NULL;
11310                         gint quote_len = 0, is_normal = 0;
11311                         start_line = *iter;
11312                         gtk_text_iter_set_line_offset(&start_line, 0); 
11313                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11314                         g_strstrip(tmp);
11315
11316                         if (*tmp == '\0') {
11317                                 is_normal = 1;
11318                         } else {
11319                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11320                                 if (quote)
11321                                         is_normal = 1;
11322                                 g_free(quote);
11323                         }
11324                         g_free(tmp);
11325                         
11326                         if (is_normal) {
11327                                 gtk_text_buffer_insert(buffer, iter, text, len);
11328                         } else {
11329                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11330                                         iter, text, len, "no_join", NULL);
11331                         }
11332                 }
11333         }
11334         
11335         if (!paste_as_quotation) {
11336                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11337                 compose_beautify_paragraph(compose, iter, FALSE);
11338                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11339                 gtk_text_buffer_delete_mark(buffer, mark);
11340         }
11341
11342         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11343                                           G_CALLBACK(text_inserted),
11344                                           compose);
11345         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11346
11347         if (compose_can_autosave(compose) && 
11348             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11349             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11350                 compose->draft_timeout_tag = g_timeout_add
11351                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11352 }
11353
11354 #if USE_ENCHANT
11355 static void compose_check_all(GtkAction *action, gpointer data)
11356 {
11357         Compose *compose = (Compose *)data;
11358         if (!compose->gtkaspell)
11359                 return;
11360                 
11361         if (gtk_widget_has_focus(compose->subject_entry))
11362                 claws_spell_entry_check_all(
11363                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11364         else
11365                 gtkaspell_check_all(compose->gtkaspell);
11366 }
11367
11368 static void compose_highlight_all(GtkAction *action, gpointer data)
11369 {
11370         Compose *compose = (Compose *)data;
11371         if (compose->gtkaspell) {
11372                 claws_spell_entry_recheck_all(
11373                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11374                 gtkaspell_highlight_all(compose->gtkaspell);
11375         }
11376 }
11377
11378 static void compose_check_backwards(GtkAction *action, gpointer data)
11379 {
11380         Compose *compose = (Compose *)data;
11381         if (!compose->gtkaspell) {
11382                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11383                 return;
11384         }
11385
11386         if (gtk_widget_has_focus(compose->subject_entry))
11387                 claws_spell_entry_check_backwards(
11388                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11389         else
11390                 gtkaspell_check_backwards(compose->gtkaspell);
11391 }
11392
11393 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11394 {
11395         Compose *compose = (Compose *)data;
11396         if (!compose->gtkaspell) {
11397                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11398                 return;
11399         }
11400
11401         if (gtk_widget_has_focus(compose->subject_entry))
11402                 claws_spell_entry_check_forwards_go(
11403                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11404         else
11405                 gtkaspell_check_forwards_go(compose->gtkaspell);
11406 }
11407 #endif
11408
11409 /*!
11410  *\brief        Guess originating forward account from MsgInfo and several 
11411  *              "common preference" settings. Return NULL if no guess. 
11412  */
11413 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11414 {
11415         PrefsAccount *account = NULL;
11416         
11417         cm_return_val_if_fail(msginfo, NULL);
11418         cm_return_val_if_fail(msginfo->folder, NULL);
11419         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11420
11421         if (msginfo->folder->prefs->enable_default_account)
11422                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11423                 
11424         if (!account) 
11425                 account = msginfo->folder->folder->account;
11426                 
11427         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11428                 gchar *to;
11429                 Xstrdup_a(to, msginfo->to, return NULL);
11430                 extract_address(to);
11431                 account = account_find_from_address(to, FALSE);
11432         }
11433
11434         if (!account && prefs_common.forward_account_autosel) {
11435                 gchar cc[BUFFSIZE];
11436                 if (!procheader_get_header_from_msginfo
11437                         (msginfo, cc,sizeof cc , "Cc:")) { 
11438                         gchar *buf = cc + strlen("Cc:");
11439                         extract_address(buf);
11440                         account = account_find_from_address(buf, FALSE);
11441                 }
11442         }
11443         
11444         if (!account && prefs_common.forward_account_autosel) {
11445                 gchar deliveredto[BUFFSIZE];
11446                 if (!procheader_get_header_from_msginfo
11447                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11448                         gchar *buf = deliveredto + strlen("Delivered-To:");
11449                         extract_address(buf);
11450                         account = account_find_from_address(buf, FALSE);
11451                 }
11452         }
11453         
11454         return account;
11455 }
11456
11457 gboolean compose_close(Compose *compose)
11458 {
11459         gint x, y;
11460
11461         cm_return_val_if_fail(compose, FALSE);
11462
11463         if (!g_mutex_trylock(compose->mutex)) {
11464                 /* we have to wait for the (possibly deferred by auto-save)
11465                  * drafting to be done, before destroying the compose under
11466                  * it. */
11467                 debug_print("waiting for drafting to finish...\n");
11468                 compose_allow_user_actions(compose, FALSE);
11469                 if (compose->close_timeout_tag == 0) {
11470                         compose->close_timeout_tag = 
11471                                 g_timeout_add (500, (GSourceFunc) compose_close,
11472                                 compose);
11473                 }
11474                 return FALSE;
11475         }
11476         
11477         if (compose->close_timeout_tag) {
11478                 /* let the close be done by the deferred callback */
11479                 g_mutex_unlock(compose->mutex);
11480                 return FALSE;
11481         }
11482
11483         gtkut_widget_get_uposition(compose->window, &x, &y);
11484         if (!compose->batch) {
11485                 prefs_common.compose_x = x;
11486                 prefs_common.compose_y = y;
11487         }
11488         g_mutex_unlock(compose->mutex);
11489         compose_destroy(compose);
11490         return FALSE;
11491 }
11492
11493 /**
11494  * Add entry field for each address in list.
11495  * \param compose     E-Mail composition object.
11496  * \param listAddress List of (formatted) E-Mail addresses.
11497  */
11498 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11499         GList *node;
11500         gchar *addr;
11501         node = listAddress;
11502         while( node ) {
11503                 addr = ( gchar * ) node->data;
11504                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11505                 node = g_list_next( node );
11506         }
11507 }
11508
11509 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11510                                     guint action, gboolean opening_multiple)
11511 {
11512         gchar *body = NULL;
11513         GSList *new_msglist = NULL;
11514         MsgInfo *tmp_msginfo = NULL;
11515         gboolean originally_enc = FALSE;
11516         gboolean originally_sig = FALSE;
11517         Compose *compose = NULL;
11518         gchar *s_system = NULL;
11519
11520         cm_return_if_fail(msgview != NULL);
11521
11522         cm_return_if_fail(msginfo_list != NULL);
11523
11524         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11525                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11526                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11527
11528                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11529                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11530                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11531                                                 orig_msginfo, mimeinfo);
11532                         if (tmp_msginfo != NULL) {
11533                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11534
11535                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11536                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11537                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11538
11539                                 tmp_msginfo->folder = orig_msginfo->folder;
11540                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11541                                 if (orig_msginfo->tags) {
11542                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11543                                         tmp_msginfo->folder->tags_dirty = TRUE;
11544                                 }
11545                         }
11546                 }
11547         }
11548
11549         if (!opening_multiple)
11550                 body = messageview_get_selection(msgview);
11551
11552         if (new_msglist) {
11553                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11554                 procmsg_msginfo_free(tmp_msginfo);
11555                 g_slist_free(new_msglist);
11556         } else
11557                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11558
11559         if (compose && originally_enc) {
11560                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11561         }
11562
11563         if (compose && originally_sig && compose->account->default_sign_reply) {
11564                 compose_force_signing(compose, compose->account, s_system);
11565         }
11566         g_free(s_system);
11567         g_free(body);
11568         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11569 }
11570
11571 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11572                                     guint action)
11573 {
11574         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11575         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11576                 GSList *cur = msginfo_list;
11577                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11578                                                "messages. Opening the windows "
11579                                                "could take some time. Do you "
11580                                                "want to continue?"), 
11581                                                g_slist_length(msginfo_list));
11582                 if (g_slist_length(msginfo_list) > 9
11583                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11584                     != G_ALERTALTERNATE) {
11585                         g_free(msg);
11586                         return;
11587                 }
11588                 g_free(msg);
11589                 /* We'll open multiple compose windows */
11590                 /* let the WM place the next windows */
11591                 compose_force_window_origin = FALSE;
11592                 for (; cur; cur = cur->next) {
11593                         GSList tmplist;
11594                         tmplist.data = cur->data;
11595                         tmplist.next = NULL;
11596                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11597                 }
11598                 compose_force_window_origin = TRUE;
11599         } else {
11600                 /* forwarding multiple mails as attachments is done via a
11601                  * single compose window */
11602                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11603         }
11604 }
11605
11606 void compose_check_for_email_account(Compose *compose)
11607 {
11608         PrefsAccount *ac = NULL, *curr = NULL;
11609         GList *list;
11610         
11611         if (!compose)
11612                 return;
11613
11614         if (compose->account && compose->account->protocol == A_NNTP) {
11615                 ac = account_get_cur_account();
11616                 if (ac->protocol == A_NNTP) {
11617                         list = account_get_list();
11618                         
11619                         for( ; list != NULL ; list = g_list_next(list)) {
11620                                 curr = (PrefsAccount *) list->data;
11621                                 if (curr->protocol != A_NNTP) {
11622                                         ac = curr;
11623                                         break;
11624                                 }
11625                         }
11626                 }
11627                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11628                                         ac->account_id); 
11629         }
11630 }
11631
11632 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11633                                 const gchar *address)
11634 {
11635         GSList *msginfo_list = NULL;
11636         gchar *body =  messageview_get_selection(msgview);
11637         Compose *compose;
11638         
11639         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11640         
11641         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11642         compose_check_for_email_account(compose);
11643         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11644         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11645         compose_reply_set_subject(compose, msginfo);
11646
11647         g_free(body);
11648         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11649 }
11650
11651 void compose_set_position(Compose *compose, gint pos)
11652 {
11653         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11654
11655         gtkut_text_view_set_position(text, pos);
11656 }
11657
11658 gboolean compose_search_string(Compose *compose,
11659                                 const gchar *str, gboolean case_sens)
11660 {
11661         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11662
11663         return gtkut_text_view_search_string(text, str, case_sens);
11664 }
11665
11666 gboolean compose_search_string_backward(Compose *compose,
11667                                 const gchar *str, gboolean case_sens)
11668 {
11669         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11670
11671         return gtkut_text_view_search_string_backward(text, str, case_sens);
11672 }
11673
11674 /* allocate a msginfo structure and populate its data from a compose data structure */
11675 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11676 {
11677         MsgInfo *newmsginfo;
11678         GSList *list;
11679         gchar buf[BUFFSIZE];
11680
11681         cm_return_val_if_fail( compose != NULL, NULL );
11682
11683         newmsginfo = procmsg_msginfo_new();
11684
11685         /* date is now */
11686         get_rfc822_date(buf, sizeof(buf));
11687         newmsginfo->date = g_strdup(buf);
11688
11689         /* from */
11690         if (compose->from_name) {
11691                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11692                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11693         }
11694
11695         /* subject */
11696         if (compose->subject_entry)
11697                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11698
11699         /* to, cc, reply-to, newsgroups */
11700         for (list = compose->header_list; list; list = list->next) {
11701                 gchar *header = gtk_editable_get_chars(
11702                                                                 GTK_EDITABLE(
11703                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11704                 gchar *entry = gtk_editable_get_chars(
11705                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11706
11707                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11708                         if ( newmsginfo->to == NULL ) {
11709                                 newmsginfo->to = g_strdup(entry);
11710                         } else if (entry && *entry) {
11711                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11712                                 g_free(newmsginfo->to);
11713                                 newmsginfo->to = tmp;
11714                         }
11715                 } else
11716                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11717                         if ( newmsginfo->cc == NULL ) {
11718                                 newmsginfo->cc = g_strdup(entry);
11719                         } else if (entry && *entry) {
11720                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11721                                 g_free(newmsginfo->cc);
11722                                 newmsginfo->cc = tmp;
11723                         }
11724                 } else
11725                 if ( strcasecmp(header,
11726                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11727                         if ( newmsginfo->newsgroups == NULL ) {
11728                                 newmsginfo->newsgroups = g_strdup(entry);
11729                         } else if (entry && *entry) {
11730                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11731                                 g_free(newmsginfo->newsgroups);
11732                                 newmsginfo->newsgroups = tmp;
11733                         }
11734                 }
11735
11736                 g_free(header);
11737                 g_free(entry);  
11738         }
11739
11740         /* other data is unset */
11741
11742         return newmsginfo;
11743 }
11744
11745 #ifdef USE_ENCHANT
11746 /* update compose's dictionaries from folder dict settings */
11747 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11748                                                 FolderItem *folder_item)
11749 {
11750         cm_return_if_fail(compose != NULL);
11751
11752         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11753                 FolderItemPrefs *prefs = folder_item->prefs;
11754
11755                 if (prefs->enable_default_dictionary)
11756                         gtkaspell_change_dict(compose->gtkaspell,
11757                                         prefs->default_dictionary, FALSE);
11758                 if (folder_item->prefs->enable_default_alt_dictionary)
11759                         gtkaspell_change_alt_dict(compose->gtkaspell,
11760                                         prefs->default_alt_dictionary);
11761                 if (prefs->enable_default_dictionary
11762                         || prefs->enable_default_alt_dictionary)
11763                         compose_spell_menu_changed(compose);
11764         }
11765 }
11766 #endif
11767
11768 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11769 {
11770         Compose *compose = (Compose *)data;
11771
11772         cm_return_if_fail(compose != NULL);
11773
11774         gtk_widget_grab_focus(compose->text);
11775 }
11776
11777 /*
11778  * End of Source.
11779  */