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