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