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