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