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