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