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