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