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