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