2007-08-08 [wwp] 2.10.0cvs104
[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         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                         gchar *msg = g_strdup_printf(err_msg, line);
2675                         alertpanel_error(msg);
2676                         g_free(msg);
2677                         goto error;
2678                 }
2679         } else
2680                 buf = "";
2681
2682         prev_autowrap = compose->autowrap;
2683         compose->autowrap = FALSE;
2684
2685         mark = gtk_text_buffer_get_insert(buffer);
2686         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2687         if (g_utf8_validate(buf, -1, NULL)) { 
2688                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2689         } else {
2690                 gchar *tmpout = NULL;
2691                 tmpout = conv_codeset_strdup
2692                         (buf, conv_get_locale_charset_str_no_utf8(),
2693                          CS_INTERNAL);
2694                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2695                         g_free(tmpout);
2696                         tmpout = g_malloc(strlen(buf)*2+1);
2697                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2698                 }
2699                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2700                 g_free(tmpout);
2701         }
2702
2703         cursor_pos = quote_fmt_get_cursor_pos();
2704         compose->set_cursor_pos = cursor_pos;
2705         if (cursor_pos == -1) {
2706                 cursor_pos = 0;
2707         }
2708         gtk_text_buffer_get_start_iter(buffer, &iter);
2709         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2710         gtk_text_buffer_place_cursor(buffer, &iter);
2711
2712         compose->autowrap = prev_autowrap;
2713         if (compose->autowrap && rewrap)
2714                 compose_wrap_all(compose);
2715
2716         goto ok;
2717
2718 error:
2719         buf = NULL;
2720 ok:
2721         SIGNAL_UNBLOCK(buffer);
2722
2723         procmsg_msginfo_free( dummyinfo );
2724
2725         return buf;
2726 }
2727
2728 /* if ml_post is of type addr@host and from is of type
2729  * addr-anything@host, return TRUE
2730  */
2731 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2732 {
2733         gchar *left_ml = NULL;
2734         gchar *right_ml = NULL;
2735         gchar *left_from = NULL;
2736         gchar *right_from = NULL;
2737         gboolean result = FALSE;
2738         
2739         if (!ml_post || !from)
2740                 return FALSE;
2741         
2742         left_ml = g_strdup(ml_post);
2743         if (strstr(left_ml, "@")) {
2744                 right_ml = strstr(left_ml, "@")+1;
2745                 *(strstr(left_ml, "@")) = '\0';
2746         }
2747         
2748         left_from = g_strdup(from);
2749         if (strstr(left_from, "@")) {
2750                 right_from = strstr(left_from, "@")+1;
2751                 *(strstr(left_from, "@")) = '\0';
2752         }
2753         
2754         if (left_ml && left_from && right_ml && right_from
2755         &&  !strncmp(left_from, left_ml, strlen(left_ml))
2756         &&  !strcmp(right_from, right_ml)) {
2757                 result = TRUE;
2758         }
2759         g_free(left_ml);
2760         g_free(left_from);
2761         
2762         return result;
2763 }
2764
2765 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2766 {
2767         gchar *my_addr1, *my_addr2;
2768         
2769         if (!addr1 || !addr2)
2770                 return FALSE;
2771
2772         Xstrdup_a(my_addr1, addr1, return FALSE);
2773         Xstrdup_a(my_addr2, addr2, return FALSE);
2774         
2775         extract_address(my_addr1);
2776         extract_address(my_addr2);
2777         
2778         return !strcasecmp(my_addr1, my_addr2);
2779 }
2780
2781 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2782                                     gboolean to_all, gboolean to_ml,
2783                                     gboolean to_sender,
2784                                     gboolean followup_and_reply_to)
2785 {
2786         GSList *cc_list = NULL;
2787         GSList *cur;
2788         gchar *from = NULL;
2789         gchar *replyto = NULL;
2790         GHashTable *to_table;
2791
2792         gboolean reply_to_ml = FALSE;
2793         gboolean default_reply_to = FALSE;
2794
2795         g_return_if_fail(compose->account != NULL);
2796         g_return_if_fail(msginfo != NULL);
2797
2798         reply_to_ml = to_ml && compose->ml_post;
2799
2800         default_reply_to = msginfo->folder && 
2801                 msginfo->folder->prefs->enable_default_reply_to;
2802
2803         if (compose->account->protocol != A_NNTP) {
2804                 if (reply_to_ml && !default_reply_to) {
2805                         
2806                         gboolean is_subscr = is_subscription(compose->ml_post,
2807                                                              msginfo->from);
2808                         if (!is_subscr) {
2809                                 /* normal answer to ml post with a reply-to */
2810                                 compose_entry_append(compose,
2811                                            compose->ml_post,
2812                                            COMPOSE_TO);
2813                                 if (compose->replyto
2814                                 &&  !same_address(compose->ml_post, compose->replyto))
2815                                         compose_entry_append(compose,
2816                                                 compose->replyto,
2817                                                 COMPOSE_CC);
2818                         } else {
2819                                 /* answer to subscription confirmation */
2820                                 if (compose->replyto)
2821                                         compose_entry_append(compose,
2822                                                 compose->replyto,
2823                                                 COMPOSE_TO);
2824                                 else if (msginfo->from)
2825                                         compose_entry_append(compose,
2826                                                 msginfo->from,
2827                                                 COMPOSE_TO);
2828                         }
2829                 }
2830                 else if (!(to_all || to_sender) && default_reply_to) {
2831                         compose_entry_append(compose,
2832                             msginfo->folder->prefs->default_reply_to,
2833                             COMPOSE_TO);
2834                         compose_entry_mark_default_to(compose,
2835                                 msginfo->folder->prefs->default_reply_to);
2836                 } else {
2837                         gchar *tmp1 = NULL;
2838                         if (!msginfo->from)
2839                                 return;
2840                         Xstrdup_a(tmp1, msginfo->from, return);
2841                         extract_address(tmp1);
2842                         if (to_all || to_sender ||
2843                             !account_find_from_address(tmp1))
2844                                 compose_entry_append(compose,
2845                                  (compose->replyto && !to_sender)
2846                                           ? compose->replyto :
2847                                           msginfo->from ? msginfo->from : "",
2848                                           COMPOSE_TO);
2849                         else if (!to_all && !to_sender) {
2850                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2851                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2852                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2853                                         compose_entry_append(compose,
2854                                                   msginfo->from ? msginfo->from : "",
2855                                                   COMPOSE_TO);
2856                                 } else {
2857                                         /* replying to own mail, use original recp */
2858                                         compose_entry_append(compose,
2859                                                   msginfo->to ? msginfo->to : "",
2860                                                   COMPOSE_TO);
2861                                         compose_entry_append(compose,
2862                                                   msginfo->cc ? msginfo->cc : "",
2863                                                   COMPOSE_CC);
2864                                 }
2865                         }
2866                 }
2867         } else {
2868                 if (to_sender || (compose->followup_to && 
2869                         !strncmp(compose->followup_to, "poster", 6)))
2870                         compose_entry_append
2871                                 (compose, 
2872                                  (compose->replyto ? compose->replyto :
2873                                         msginfo->from ? msginfo->from : ""),
2874                                  COMPOSE_TO);
2875                                  
2876                 else if (followup_and_reply_to || to_all) {
2877                         compose_entry_append
2878                                 (compose,
2879                                  (compose->replyto ? compose->replyto :
2880                                  msginfo->from ? msginfo->from : ""),
2881                                  COMPOSE_TO);                           
2882                 
2883                         compose_entry_append
2884                                 (compose,
2885                                  compose->followup_to ? compose->followup_to :
2886                                  compose->newsgroups ? compose->newsgroups : "",
2887                                  COMPOSE_NEWSGROUPS);
2888                 } 
2889                 else 
2890                         compose_entry_append
2891                                 (compose,
2892                                  compose->followup_to ? compose->followup_to :
2893                                  compose->newsgroups ? compose->newsgroups : "",
2894                                  COMPOSE_NEWSGROUPS);
2895         }
2896
2897         if (msginfo->subject && *msginfo->subject) {
2898                 gchar *buf, *buf2;
2899                 gchar *p;
2900
2901                 buf = p = g_strdup(msginfo->subject);
2902                 p += subject_get_prefix_length(p);
2903                 memmove(buf, p, strlen(p) + 1);
2904
2905                 buf2 = g_strdup_printf("Re: %s", buf);
2906                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2907
2908                 g_free(buf2);
2909                 g_free(buf);
2910         } else
2911                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2912
2913         if (to_ml && compose->ml_post) return;
2914         if (!to_all || compose->account->protocol == A_NNTP) return;
2915
2916         if (compose->replyto) {
2917                 Xstrdup_a(replyto, compose->replyto, return);
2918                 extract_address(replyto);
2919         }
2920         if (msginfo->from) {
2921                 Xstrdup_a(from, msginfo->from, return);
2922                 extract_address(from);
2923         }
2924
2925         if (replyto && from)
2926                 cc_list = address_list_append_with_comments(cc_list, from);
2927         if (to_all && msginfo->folder && 
2928             msginfo->folder->prefs->enable_default_reply_to)
2929                 cc_list = address_list_append_with_comments(cc_list,
2930                                 msginfo->folder->prefs->default_reply_to);
2931         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2932         cc_list = address_list_append_with_comments(cc_list, compose->cc);
2933
2934         to_table = g_hash_table_new(g_str_hash, g_str_equal);
2935         if (replyto)
2936                 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2937         if (compose->account) {
2938                 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2939                                     GINT_TO_POINTER(1));
2940         }
2941         /* remove address on To: and that of current account */
2942         for (cur = cc_list; cur != NULL; ) {
2943                 GSList *next = cur->next;
2944                 gchar *addr;
2945
2946                 addr = g_utf8_strdown(cur->data, -1);
2947                 extract_address(addr);
2948
2949                 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2950                         cc_list = g_slist_remove(cc_list, cur->data);
2951                 else
2952                         g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2953
2954                 cur = next;
2955         }
2956         hash_free_strings(to_table);
2957         g_hash_table_destroy(to_table);
2958
2959         if (cc_list) {
2960                 for (cur = cc_list; cur != NULL; cur = cur->next)
2961                         compose_entry_append(compose, (gchar *)cur->data,
2962                                              COMPOSE_CC);
2963                 slist_free_strings(cc_list);
2964                 g_slist_free(cc_list);
2965         }
2966
2967 }
2968
2969 #define SET_ENTRY(entry, str) \
2970 { \
2971         if (str && *str) \
2972                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
2973 }
2974
2975 #define SET_ADDRESS(type, str) \
2976 { \
2977         if (str && *str) \
2978                 compose_entry_append(compose, str, type); \
2979 }
2980
2981 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
2982 {
2983         g_return_if_fail(msginfo != NULL);
2984
2985         SET_ENTRY(subject_entry, msginfo->subject);
2986         SET_ENTRY(from_name, msginfo->from);
2987         SET_ADDRESS(COMPOSE_TO, msginfo->to);
2988         SET_ADDRESS(COMPOSE_CC, compose->cc);
2989         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
2990         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
2991         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
2992         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
2993
2994         compose_update_priority_menu_item(compose);
2995         compose_update_privacy_system_menu_item(compose, FALSE);
2996         compose_show_first_last_header(compose, TRUE);
2997 }
2998
2999 #undef SET_ENTRY
3000 #undef SET_ADDRESS
3001
3002 static void compose_insert_sig(Compose *compose, gboolean replace)
3003 {
3004         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3005         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3006         GtkTextMark *mark;
3007         GtkTextIter iter, iter_end;
3008         gint cur_pos;
3009         gchar *search = NULL;
3010         gboolean prev_autowrap;
3011         gboolean found = FALSE, shift = FALSE;
3012
3013         
3014         g_return_if_fail(compose->account != NULL);
3015
3016         BLOCK_WRAP();
3017
3018         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3019                                         G_CALLBACK(compose_changed_cb),
3020                                         compose);
3021         
3022         mark = gtk_text_buffer_get_insert(buffer);
3023         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3024         cur_pos = gtk_text_iter_get_offset (&iter);
3025
3026         gtk_text_buffer_get_end_iter(buffer, &iter);
3027
3028         search = compose->sig_str;
3029 again:
3030         if (replace && search) {
3031                 GtkTextIter first_iter, start_iter, end_iter;
3032
3033                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3034
3035                 if (compose->sig_str[0] == '\0')
3036                         found = FALSE;
3037                 else
3038                         found = gtk_text_iter_forward_search(&first_iter,
3039                                                              search,
3040                                                              GTK_TEXT_SEARCH_TEXT_ONLY,
3041                                                              &start_iter, &end_iter,
3042                                                              NULL);
3043
3044                 if (found) {
3045                         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3046                         iter = start_iter;
3047                 }
3048         } 
3049         if (replace && !found && search && strlen(search) > 2
3050         &&  search[0] == '\n' && search[1] == '\n') {
3051                 search ++;
3052                 shift = TRUE;
3053                 goto again;
3054         }
3055
3056         g_free(compose->sig_str);
3057         compose->sig_str = compose_get_signature_str(compose);
3058         if (!compose->sig_str || (replace && !compose->account->auto_sig))
3059                 compose->sig_str = g_strdup("");
3060
3061         cur_pos = gtk_text_iter_get_offset(&iter);
3062         if (shift && found)
3063                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str + 1, -1);
3064         else
3065                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3066         /* skip \n\n */
3067         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3068         gtk_text_iter_forward_char(&iter);
3069         gtk_text_iter_forward_char(&iter);
3070         gtk_text_buffer_get_end_iter(buffer, &iter_end);
3071         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3072
3073         if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3074                 cur_pos = gtk_text_buffer_get_char_count (buffer);
3075
3076         /* put the cursor where it should be 
3077          * either where the quote_fmt says, either before the signature */
3078         if (compose->set_cursor_pos < 0)
3079                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3080         else
3081                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3082                         compose->set_cursor_pos);
3083                 
3084         gtk_text_buffer_place_cursor(buffer, &iter);
3085         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3086                                         G_CALLBACK(compose_changed_cb),
3087                                         compose);
3088                 
3089         UNBLOCK_WRAP();
3090 }
3091
3092 static gchar *compose_get_signature_str(Compose *compose)
3093 {
3094         gchar *sig_body = NULL;
3095         gchar *sig_str = NULL;
3096         gchar *utf8_sig_str = NULL;
3097
3098         g_return_val_if_fail(compose->account != NULL, NULL);
3099
3100         if (!compose->account->sig_path)
3101                 return NULL;
3102
3103         if (compose->account->sig_type == SIG_FILE) {
3104                 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3105                         g_warning("can't open signature file: %s\n",
3106                                   compose->account->sig_path);
3107                         return NULL;
3108                 }
3109         }
3110
3111         if (compose->account->sig_type == SIG_COMMAND)
3112                 sig_body = get_command_output(compose->account->sig_path);
3113         else {
3114                 gchar *tmp;
3115
3116                 tmp = file_read_to_str(compose->account->sig_path);
3117                 if (!tmp)
3118                         return NULL;
3119                 sig_body = normalize_newlines(tmp);
3120                 g_free(tmp);
3121         }
3122
3123         if (compose->account->sig_sep) {
3124                 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3125                                       NULL);
3126                 g_free(sig_body);
3127         } else
3128                 sig_str = g_strconcat("\n\n", sig_body, NULL);
3129
3130         if (sig_str) {
3131                 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3132                         utf8_sig_str = sig_str;
3133                 else {
3134                         utf8_sig_str = conv_codeset_strdup
3135                                 (sig_str, conv_get_locale_charset_str_no_utf8(),
3136                                  CS_INTERNAL);
3137                         g_free(sig_str);
3138                 }
3139         }
3140
3141         return utf8_sig_str;
3142 }
3143
3144 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3145 {
3146         GtkTextView *text;
3147         GtkTextBuffer *buffer;
3148         GtkTextMark *mark;
3149         GtkTextIter iter;
3150         const gchar *cur_encoding;
3151         gchar buf[BUFFSIZE];
3152         gint len;
3153         FILE *fp;
3154         gboolean prev_autowrap;
3155         gboolean badtxt = FALSE;
3156
3157         g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3158
3159         if ((fp = g_fopen(file, "rb")) == NULL) {
3160                 FILE_OP_ERROR(file, "fopen");
3161                 return COMPOSE_INSERT_READ_ERROR;
3162         }
3163
3164         prev_autowrap = compose->autowrap;
3165         compose->autowrap = FALSE;
3166
3167         text = GTK_TEXT_VIEW(compose->text);
3168         buffer = gtk_text_view_get_buffer(text);
3169         mark = gtk_text_buffer_get_insert(buffer);
3170         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3171
3172         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3173                                         G_CALLBACK(text_inserted),
3174                                         compose);
3175
3176         cur_encoding = conv_get_locale_charset_str_no_utf8();
3177
3178         while (fgets(buf, sizeof(buf), fp) != NULL) {
3179                 gchar *str;
3180
3181                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3182                         str = g_strdup(buf);
3183                 else
3184                         str = conv_codeset_strdup
3185                                 (buf, cur_encoding, CS_INTERNAL);
3186                 if (!str) continue;
3187
3188                 /* strip <CR> if DOS/Windows file,
3189                    replace <CR> with <LF> if Macintosh file. */
3190                 strcrchomp(str);
3191                 len = strlen(str);
3192                 if (len > 0 && str[len - 1] != '\n') {
3193                         while (--len >= 0)
3194                                 if (str[len] == '\r') str[len] = '\n';
3195                 }
3196
3197                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3198                 g_free(str);
3199         }
3200
3201         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3202                                           G_CALLBACK(text_inserted),
3203                                           compose);
3204         compose->autowrap = prev_autowrap;
3205         if (compose->autowrap)
3206                 compose_wrap_all(compose);
3207
3208         fclose(fp);
3209
3210         if (badtxt)
3211                 return COMPOSE_INSERT_INVALID_CHARACTER;
3212         else 
3213                 return COMPOSE_INSERT_SUCCESS;
3214 }
3215
3216 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3217                                   const gchar *filename,
3218                                   const gchar *content_type)
3219 {
3220         AttachInfo *ainfo;
3221         GtkTreeIter iter;
3222         FILE *fp;
3223         off_t size;
3224         GAuto *auto_ainfo;
3225         gchar *size_text;
3226         GtkListStore *store;
3227         gchar *name;
3228         gboolean has_binary = FALSE;
3229
3230         if (!is_file_exist(file)) {
3231                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3232                 gboolean result = FALSE;
3233                 if (file_from_uri && is_file_exist(file_from_uri)) {
3234                         result = compose_attach_append(
3235                                                 compose, file_from_uri,
3236                                                 filename,
3237                                                 content_type);
3238                 }
3239                 g_free(file_from_uri);
3240                 if (result)
3241                         return TRUE;
3242                 alertpanel_error("File %s doesn't exist\n", filename);
3243                 return FALSE;
3244         }
3245         if ((size = get_file_size(file)) < 0) {
3246                 alertpanel_error("Can't get file size of %s\n", filename);
3247                 return FALSE;
3248         }
3249         if (size == 0) {
3250                 alertpanel_error(_("File %s is empty."), filename);
3251                 return FALSE;
3252         }
3253         if ((fp = g_fopen(file, "rb")) == NULL) {
3254                 alertpanel_error(_("Can't read %s."), filename);
3255                 return FALSE;
3256         }
3257         fclose(fp);
3258
3259         ainfo = g_new0(AttachInfo, 1);
3260         auto_ainfo = g_auto_pointer_new_with_free
3261                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3262         ainfo->file = g_strdup(file);
3263
3264         if (content_type) {
3265                 ainfo->content_type = g_strdup(content_type);
3266                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3267                         MsgInfo *msginfo;
3268                         MsgFlags flags = {0, 0};
3269
3270                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3271                                 ainfo->encoding = ENC_7BIT;
3272                         else
3273                                 ainfo->encoding = ENC_8BIT;
3274
3275                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3276                         if (msginfo && msginfo->subject)
3277                                 name = g_strdup(msginfo->subject);
3278                         else
3279                                 name = g_path_get_basename(filename ? filename : file);
3280
3281                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3282
3283                         procmsg_msginfo_free(msginfo);
3284                 } else {
3285                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3286                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3287                         else
3288                                 ainfo->encoding = ENC_BASE64;
3289                         name = g_path_get_basename(filename ? filename : file);
3290                         ainfo->name = g_strdup(name);
3291                 }
3292                 g_free(name);
3293         } else {
3294                 ainfo->content_type = procmime_get_mime_type(file);
3295                 if (!ainfo->content_type) {
3296                         ainfo->content_type =
3297                                 g_strdup("application/octet-stream");
3298                         ainfo->encoding = ENC_BASE64;
3299                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3300                         ainfo->encoding =
3301                                 procmime_get_encoding_for_text_file(file, &has_binary);
3302                 else
3303                         ainfo->encoding = ENC_BASE64;
3304                 name = g_path_get_basename(filename ? filename : file);
3305                 ainfo->name = g_strdup(name);   
3306                 g_free(name);
3307         }
3308
3309         if (ainfo->name != NULL
3310         &&  !strcmp(ainfo->name, ".")) {
3311                 g_free(ainfo->name);
3312                 ainfo->name = NULL;
3313         }
3314
3315         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3316                 g_free(ainfo->content_type);
3317                 ainfo->content_type = g_strdup("application/octet-stream");
3318         }
3319
3320         ainfo->size = size;
3321         size_text = to_human_readable(size);
3322
3323         store = GTK_LIST_STORE(gtk_tree_view_get_model
3324                         (GTK_TREE_VIEW(compose->attach_clist)));
3325                 
3326         gtk_list_store_append(store, &iter);
3327         gtk_list_store_set(store, &iter, 
3328                            COL_MIMETYPE, ainfo->content_type,
3329                            COL_SIZE, size_text,
3330                            COL_NAME, ainfo->name,
3331                            COL_DATA, ainfo,
3332                            COL_AUTODATA, auto_ainfo,
3333                            -1);
3334         
3335         g_auto_pointer_free(auto_ainfo);
3336         return TRUE;
3337 }
3338
3339 static void compose_use_signing(Compose *compose, gboolean use_signing)
3340 {
3341         GtkItemFactory *ifactory;
3342         GtkWidget *menuitem = NULL;
3343
3344         compose->use_signing = use_signing;
3345         ifactory = gtk_item_factory_from_widget(compose->menubar);
3346         menuitem = gtk_item_factory_get_item
3347                 (ifactory, "/Options/Sign");
3348         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3349                                        use_signing);
3350 }
3351
3352 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3353 {
3354         GtkItemFactory *ifactory;
3355         GtkWidget *menuitem = NULL;
3356
3357         compose->use_encryption = use_encryption;
3358         ifactory = gtk_item_factory_from_widget(compose->menubar);
3359         menuitem = gtk_item_factory_get_item
3360                 (ifactory, "/Options/Encrypt");
3361
3362         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3363                                        use_encryption);
3364 }
3365
3366 #define NEXT_PART_NOT_CHILD(info)  \
3367 {  \
3368         node = info->node;  \
3369         while (node->children)  \
3370                 node = g_node_last_child(node);  \
3371         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3372 }
3373
3374 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3375 {
3376         MimeInfo *mimeinfo;
3377         MimeInfo *child;
3378         MimeInfo *firsttext = NULL;
3379         MimeInfo *encrypted = NULL;
3380         GNode    *node;
3381         gchar *outfile;
3382         const gchar *partname = NULL;
3383
3384         mimeinfo = procmime_scan_message(msginfo);
3385         if (!mimeinfo) return;
3386
3387         if (mimeinfo->node->children == NULL) {
3388                 procmime_mimeinfo_free_all(mimeinfo);
3389                 return;
3390         }
3391
3392         /* find first content part */
3393         child = (MimeInfo *) mimeinfo->node->children->data;
3394         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3395                 child = (MimeInfo *)child->node->children->data;
3396
3397         if (child->type == MIMETYPE_TEXT) {
3398                 firsttext = child;
3399                 debug_print("First text part found\n");
3400         } else if (compose->mode == COMPOSE_REEDIT &&
3401                  child->type == MIMETYPE_APPLICATION &&
3402                  !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3403                 encrypted = (MimeInfo *)child->node->parent->data;
3404         }
3405      
3406         child = (MimeInfo *) mimeinfo->node->children->data;
3407         while (child != NULL) {
3408                 gint err;
3409
3410                 if (child == encrypted) {
3411                         /* skip this part of tree */
3412                         NEXT_PART_NOT_CHILD(child);
3413                         continue;
3414                 }
3415
3416                 if (child->type == MIMETYPE_MULTIPART) {
3417                         /* get the actual content */
3418                         child = procmime_mimeinfo_next(child);
3419                         continue;
3420                 }
3421                     
3422                 if (child == firsttext) {
3423                         child = procmime_mimeinfo_next(child);
3424                         continue;
3425                 }
3426
3427                 outfile = procmime_get_tmp_file_name(child);
3428                 if ((err = procmime_get_part(outfile, child)) < 0)
3429                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3430                 else {
3431                         gchar *content_type;
3432
3433                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3434
3435                         /* if we meet a pgp signature, we don't attach it, but
3436                          * we force signing. */
3437                         if ((strcmp(content_type, "application/pgp-signature") &&
3438                             strcmp(content_type, "application/pkcs7-signature") &&
3439                             strcmp(content_type, "application/x-pkcs7-signature"))
3440                             || compose->mode == COMPOSE_REDIRECT) {
3441                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3442                                 if (partname == NULL)
3443                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3444                                 if (partname == NULL)
3445                                         partname = "";
3446                                 compose_attach_append(compose, outfile, 
3447                                                       partname, content_type);
3448                         } else {
3449                                 compose_force_signing(compose, compose->account);
3450                         }
3451                         g_free(content_type);
3452                 }
3453                 g_free(outfile);
3454                 NEXT_PART_NOT_CHILD(child);
3455         }
3456         procmime_mimeinfo_free_all(mimeinfo);
3457 }
3458
3459 #undef NEXT_PART_NOT_CHILD
3460
3461
3462
3463 typedef enum {
3464         WAIT_FOR_INDENT_CHAR,
3465         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3466 } IndentState;
3467
3468 /* return indent length, we allow:
3469    indent characters followed by indent characters or spaces/tabs,
3470    alphabets and numbers immediately followed by indent characters,
3471    and the repeating sequences of the above
3472    If quote ends with multiple spaces, only the first one is included. */
3473 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3474                                     const GtkTextIter *start, gint *len)
3475 {
3476         GtkTextIter iter = *start;
3477         gunichar wc;
3478         gchar ch[6];
3479         gint clen;
3480         IndentState state = WAIT_FOR_INDENT_CHAR;
3481         gboolean is_space;
3482         gboolean is_indent;
3483         gint alnum_count = 0;
3484         gint space_count = 0;
3485         gint quote_len = 0;
3486
3487         if (prefs_common.quote_chars == NULL) {
3488                 return 0 ;
3489         }
3490
3491         while (!gtk_text_iter_ends_line(&iter)) {
3492                 wc = gtk_text_iter_get_char(&iter);
3493                 if (g_unichar_iswide(wc))
3494                         break;
3495                 clen = g_unichar_to_utf8(wc, ch);
3496                 if (clen != 1)
3497                         break;
3498
3499                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3500                 is_space = g_unichar_isspace(wc);
3501
3502                 if (state == WAIT_FOR_INDENT_CHAR) {
3503                         if (!is_indent && !g_unichar_isalnum(wc))
3504                                 break;
3505                         if (is_indent) {
3506                                 quote_len += alnum_count + space_count + 1;
3507                                 alnum_count = space_count = 0;
3508                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3509                         } else
3510                                 alnum_count++;
3511                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3512                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3513                                 break;
3514                         if (is_space)
3515                                 space_count++;
3516                         else if (is_indent) {
3517                                 quote_len += alnum_count + space_count + 1;
3518                                 alnum_count = space_count = 0;
3519                         } else {
3520                                 alnum_count++;
3521                                 state = WAIT_FOR_INDENT_CHAR;
3522                         }
3523                 }
3524
3525                 gtk_text_iter_forward_char(&iter);
3526         }
3527
3528         if (quote_len > 0 && space_count > 0)
3529                 quote_len++;
3530
3531         if (len)
3532                 *len = quote_len;
3533
3534         if (quote_len > 0) {
3535                 iter = *start;
3536                 gtk_text_iter_forward_chars(&iter, quote_len);
3537                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3538         }
3539
3540         return NULL;
3541 }
3542
3543 /* return TRUE if the line is itemized */
3544 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3545                                     const GtkTextIter *start)
3546 {
3547         GtkTextIter iter = *start;
3548         gunichar wc;
3549         gchar ch[6];
3550         gint clen;
3551
3552         if (gtk_text_iter_ends_line(&iter))
3553                 return FALSE;
3554
3555         while (1) {
3556                 wc = gtk_text_iter_get_char(&iter);
3557                 if (!g_unichar_isspace(wc))
3558                         break;
3559                 gtk_text_iter_forward_char(&iter);
3560                 if (gtk_text_iter_ends_line(&iter))
3561                         return FALSE;
3562         }
3563
3564         clen = g_unichar_to_utf8(wc, ch);
3565         if (clen != 1)
3566                 return FALSE;
3567
3568         if (!strchr("*-+", ch[0]))
3569                 return FALSE;
3570
3571         gtk_text_iter_forward_char(&iter);
3572         if (gtk_text_iter_ends_line(&iter))
3573                 return FALSE;
3574         wc = gtk_text_iter_get_char(&iter);
3575         if (g_unichar_isspace(wc))
3576                 return TRUE;
3577
3578         return FALSE;
3579 }
3580
3581 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3582                                            const GtkTextIter *start,
3583                                            GtkTextIter *break_pos,
3584                                            gint max_col,
3585                                            gint quote_len)
3586 {
3587         GtkTextIter iter = *start, line_end = *start;
3588         PangoLogAttr *attrs;
3589         gchar *str;
3590         gchar *p;
3591         gint len;
3592         gint i;
3593         gint col = 0;
3594         gint pos = 0;
3595         gboolean can_break = FALSE;
3596         gboolean do_break = FALSE;
3597         gboolean was_white = FALSE;
3598         gboolean prev_dont_break = FALSE;
3599
3600         gtk_text_iter_forward_to_line_end(&line_end);
3601         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3602         len = g_utf8_strlen(str, -1);
3603         
3604         if (len == 0) {
3605                 g_free(str);
3606                 g_warning("compose_get_line_break_pos: len = 0!\n");
3607                 return FALSE;
3608         }
3609
3610         /* g_print("breaking line: %d: %s (len = %d)\n",
3611                 gtk_text_iter_get_line(&iter), str, len); */
3612
3613         attrs = g_new(PangoLogAttr, len + 1);
3614
3615         pango_default_break(str, -1, NULL, attrs, len + 1);
3616
3617         p = str;
3618
3619         /* skip quote and leading spaces */
3620         for (i = 0; *p != '\0' && i < len; i++) {
3621                 gunichar wc;
3622
3623                 wc = g_utf8_get_char(p);
3624                 if (i >= quote_len && !g_unichar_isspace(wc))
3625                         break;
3626                 if (g_unichar_iswide(wc))
3627                         col += 2;
3628                 else if (*p == '\t')
3629                         col += 8;
3630                 else
3631                         col++;
3632                 p = g_utf8_next_char(p);
3633         }
3634
3635         for (; *p != '\0' && i < len; i++) {
3636                 PangoLogAttr *attr = attrs + i;
3637                 gunichar wc;
3638                 gint uri_len;
3639
3640                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3641                         pos = i;
3642                 
3643                 was_white = attr->is_white;
3644
3645                 /* don't wrap URI */
3646                 if ((uri_len = get_uri_len(p)) > 0) {
3647                         col += uri_len;
3648                         if (pos > 0 && col > max_col) {
3649                                 do_break = TRUE;
3650                                 break;
3651                         }
3652                         i += uri_len - 1;
3653                         p += uri_len;
3654                         can_break = TRUE;
3655                         continue;
3656                 }
3657
3658                 wc = g_utf8_get_char(p);
3659                 if (g_unichar_iswide(wc)) {
3660                         col += 2;
3661                         if (prev_dont_break && can_break && attr->is_line_break)
3662                                 pos = i;
3663                 } else if (*p == '\t')
3664                         col += 8;
3665                 else
3666                         col++;
3667                 if (pos > 0 && col > max_col) {
3668                         do_break = TRUE;
3669                         break;
3670                 }
3671
3672                 if (*p == '-' || *p == '/')
3673                         prev_dont_break = TRUE;
3674                 else
3675                         prev_dont_break = FALSE;
3676
3677                 p = g_utf8_next_char(p);
3678                 can_break = TRUE;
3679         }
3680
3681         debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3682
3683         g_free(attrs);
3684         g_free(str);
3685
3686         *break_pos = *start;
3687         gtk_text_iter_set_line_offset(break_pos, pos);
3688
3689         return do_break;
3690 }
3691
3692 static gboolean compose_join_next_line(Compose *compose,
3693                                        GtkTextBuffer *buffer,
3694                                        GtkTextIter *iter,
3695                                        const gchar *quote_str)
3696 {
3697         GtkTextIter iter_ = *iter, cur, prev, next, end;
3698         PangoLogAttr attrs[3];
3699         gchar *str;
3700         gchar *next_quote_str;
3701         gunichar wc1, wc2;
3702         gint quote_len;
3703         gboolean keep_cursor = FALSE;
3704
3705         if (!gtk_text_iter_forward_line(&iter_) ||
3706             gtk_text_iter_ends_line(&iter_))
3707                 return FALSE;
3708
3709         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
3710
3711         if ((quote_str || next_quote_str) &&
3712             strcmp2(quote_str, next_quote_str) != 0) {
3713                 g_free(next_quote_str);
3714                 return FALSE;
3715         }
3716         g_free(next_quote_str);
3717
3718         end = iter_;
3719         if (quote_len > 0) {
3720                 gtk_text_iter_forward_chars(&end, quote_len);
3721                 if (gtk_text_iter_ends_line(&end))
3722                         return FALSE;
3723         }
3724
3725         /* don't join itemized lines */
3726         if (compose_is_itemized(buffer, &end))
3727                 return FALSE;
3728
3729         /* don't join signature separator */
3730         if (compose_is_sig_separator(compose, buffer, &iter_))
3731                 return FALSE;
3732
3733         /* delete quote str */
3734         if (quote_len > 0)
3735                 gtk_text_buffer_delete(buffer, &iter_, &end);
3736
3737         /* don't join line breaks put by the user */
3738         prev = cur = iter_;
3739         gtk_text_iter_backward_char(&cur);
3740         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3741                 gtk_text_iter_forward_char(&cur);
3742                 *iter = cur;
3743                 return FALSE;
3744         }
3745         gtk_text_iter_forward_char(&cur);
3746         /* delete linebreak and extra spaces */
3747         while (gtk_text_iter_backward_char(&cur)) {
3748                 wc1 = gtk_text_iter_get_char(&cur);
3749                 if (!g_unichar_isspace(wc1))
3750                         break;
3751                 prev = cur;
3752         }
3753         next = cur = iter_;
3754         while (!gtk_text_iter_ends_line(&cur)) {
3755                 wc1 = gtk_text_iter_get_char(&cur);
3756                 if (!g_unichar_isspace(wc1))
3757                         break;
3758                 gtk_text_iter_forward_char(&cur);
3759                 next = cur;
3760         }
3761         if (!gtk_text_iter_equal(&prev, &next)) {
3762                 GtkTextMark *mark;
3763
3764                 mark = gtk_text_buffer_get_insert(buffer);
3765                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3766                 if (gtk_text_iter_equal(&prev, &cur))
3767                         keep_cursor = TRUE;
3768                 gtk_text_buffer_delete(buffer, &prev, &next);
3769         }
3770         iter_ = prev;
3771
3772         /* insert space if required */
3773         gtk_text_iter_backward_char(&prev);
3774         wc1 = gtk_text_iter_get_char(&prev);
3775         wc2 = gtk_text_iter_get_char(&next);
3776         gtk_text_iter_forward_char(&next);
3777         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3778         pango_default_break(str, -1, NULL, attrs, 3);
3779         if (!attrs[1].is_line_break ||
3780             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3781                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3782                 if (keep_cursor) {
3783                         gtk_text_iter_backward_char(&iter_);
3784                         gtk_text_buffer_place_cursor(buffer, &iter_);
3785                 }
3786         }
3787         g_free(str);
3788
3789         *iter = iter_;
3790         return TRUE;
3791 }
3792
3793 #define ADD_TXT_POS(bp_, ep_, pti_) \
3794         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3795                 last = last->next; \
3796                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3797                 last->next = NULL; \
3798         } else { \
3799                 g_warning("alloc error scanning URIs\n"); \
3800         }
3801
3802 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3803 {
3804         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3805         GtkTextBuffer *buffer;
3806         GtkTextIter iter, break_pos, end_of_line;
3807         gchar *quote_str = NULL;
3808         gint quote_len;
3809         gboolean wrap_quote = prefs_common.linewrap_quote;
3810         gboolean prev_autowrap = compose->autowrap;
3811         gint startq_offset = -1, noq_offset = -1;
3812         gint uri_start = -1, uri_stop = -1;
3813         gint nouri_start = -1, nouri_stop = -1;
3814         gint num_blocks = 0;
3815         gint quotelevel = -1;
3816         gboolean modified = force;
3817         gboolean removed = FALSE;
3818         gboolean modified_before_remove = FALSE;
3819         gint lines = 0;
3820         gboolean start = TRUE;
3821
3822         if (force) {
3823                 modified = TRUE;
3824         }
3825         if (compose->draft_timeout_tag == -2) {
3826                 modified = TRUE;
3827         }
3828
3829         compose->autowrap = FALSE;
3830
3831         buffer = gtk_text_view_get_buffer(text);
3832         undo_wrapping(compose->undostruct, TRUE);
3833         if (par_iter) {
3834                 iter = *par_iter;
3835         } else {
3836                 GtkTextMark *mark;
3837                 mark = gtk_text_buffer_get_insert(buffer);
3838                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3839         }
3840
3841
3842         if (compose->draft_timeout_tag == -2) {
3843                 if (gtk_text_iter_ends_line(&iter)) {
3844                         while (gtk_text_iter_ends_line(&iter) &&
3845                                gtk_text_iter_forward_line(&iter))
3846                                 ;
3847                 } else {
3848                         while (gtk_text_iter_backward_line(&iter)) {
3849                                 if (gtk_text_iter_ends_line(&iter)) {
3850                                         gtk_text_iter_forward_line(&iter);
3851                                         break;
3852                                 }
3853                         }
3854                 }
3855         } else {
3856                 /* move to line start */
3857                 gtk_text_iter_set_line_offset(&iter, 0);
3858         }
3859         /* go until paragraph end (empty line) */
3860         while (start || !gtk_text_iter_ends_line(&iter)) {
3861                 gchar *scanpos = NULL;
3862                 /* parse table - in order of priority */
3863                 struct table {
3864                         const gchar *needle; /* token */
3865
3866                         /* token search function */
3867                         gchar    *(*search)     (const gchar *haystack,
3868                                                  const gchar *needle);
3869                         /* part parsing function */
3870                         gboolean  (*parse)      (const gchar *start,
3871                                                  const gchar *scanpos,
3872                                                  const gchar **bp_,
3873                                                  const gchar **ep_,
3874                                                  gboolean hdr);
3875                         /* part to URI function */
3876                         gchar    *(*build_uri)  (const gchar *bp,
3877                                                  const gchar *ep);
3878                 };
3879
3880                 static struct table parser[] = {
3881                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
3882                         {"https://", strcasestr, get_uri_part,   make_uri_string},
3883                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
3884                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
3885                         {"www.",     strcasestr, get_uri_part,   make_http_string},
3886                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
3887                         {"@",        strcasestr, get_email_part, make_email_string}
3888                 };
3889                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
3890                 gint last_index = PARSE_ELEMS;
3891                 gint  n;
3892                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
3893                 gint walk_pos;
3894                 
3895                 start = FALSE;
3896                 if (!prev_autowrap && num_blocks == 0) {
3897                         num_blocks++;
3898                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3899                                         G_CALLBACK(text_inserted),
3900                                         compose);
3901                 }
3902                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
3903                         goto colorize;
3904
3905                 uri_start = uri_stop = -1;
3906                 quote_len = 0;
3907                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
3908
3909                 if (quote_str) {
3910                         debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
3911                         if (startq_offset == -1) 
3912                                 startq_offset = gtk_text_iter_get_offset(&iter);
3913                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
3914                         if (quotelevel > 2) {
3915                                 /* recycle colors */
3916                                 if (prefs_common.recycle_quote_colors)
3917                                         quotelevel %= 3;
3918                                 else
3919                                         quotelevel = 2;
3920                         }
3921                         if (!wrap_quote) {
3922                                 goto colorize;
3923                         }
3924                 } else {
3925                         if (startq_offset == -1)
3926                                 noq_offset = gtk_text_iter_get_offset(&iter);
3927                         quotelevel = -1;
3928                 }
3929
3930                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
3931                         goto colorize;
3932                 }
3933                 if (gtk_text_iter_ends_line(&iter)) {
3934                         goto colorize;
3935                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
3936                                                prefs_common.linewrap_len,
3937                                                quote_len)) {
3938                         GtkTextIter prev, next, cur;
3939
3940                         if (prev_autowrap != FALSE || force) {
3941                                 compose->automatic_break = TRUE;
3942                                 modified = TRUE;
3943                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
3944                                 compose->automatic_break = FALSE;
3945                         } else if (quote_str && wrap_quote) {
3946                                 compose->automatic_break = TRUE;
3947                                 modified = TRUE;
3948                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
3949                                 compose->automatic_break = FALSE;
3950                         } else 
3951                                 goto colorize;
3952                         /* remove trailing spaces */
3953                         cur = break_pos;
3954                         gtk_text_iter_backward_char(&cur);
3955                         prev = next = cur;
3956                         while (!gtk_text_iter_starts_line(&cur)) {
3957                                 gunichar wc;
3958
3959                                 gtk_text_iter_backward_char(&cur);
3960                                 wc = gtk_text_iter_get_char(&cur);
3961                                 if (!g_unichar_isspace(wc))
3962                                         break;
3963                                 prev = cur;
3964                         }
3965                         if (!gtk_text_iter_equal(&prev, &next)) {
3966                                 gtk_text_buffer_delete(buffer, &prev, &next);
3967                                 break_pos = next;
3968                                 gtk_text_iter_forward_char(&break_pos);
3969                         }
3970
3971                         if (quote_str)
3972                                 gtk_text_buffer_insert(buffer, &break_pos,
3973                                                        quote_str, -1);
3974
3975                         iter = break_pos;
3976                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
3977
3978                         /* move iter to current line start */
3979                         gtk_text_iter_set_line_offset(&iter, 0);
3980                         if (quote_str) {
3981                                 g_free(quote_str);
3982                                 quote_str = NULL;
3983                         }
3984                         continue;       
3985                 } else {
3986                         /* move iter to next line start */
3987                         iter = break_pos;
3988                         lines++;
3989                 }
3990
3991 colorize:
3992                 if (!prev_autowrap && num_blocks > 0) {
3993                         num_blocks--;
3994                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3995                                         G_CALLBACK(text_inserted),
3996                                         compose);
3997                 }
3998                 end_of_line = iter;
3999                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4000                         gtk_text_iter_forward_char(&end_of_line);
4001                 }
4002                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4003
4004                 nouri_start = gtk_text_iter_get_offset(&iter);
4005                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4006
4007                 walk_pos = gtk_text_iter_get_offset(&iter);
4008                 /* FIXME: this looks phony. scanning for anything in the parse table */
4009                 for (n = 0; n < PARSE_ELEMS; n++) {
4010                         gchar *tmp;
4011
4012                         tmp = parser[n].search(walk, parser[n].needle);
4013                         if (tmp) {
4014                                 if (scanpos == NULL || tmp < scanpos) {
4015                                         scanpos = tmp;
4016                                         last_index = n;
4017                                 }
4018                         }                                       
4019                 }
4020
4021                 bp = ep = 0;
4022                 if (scanpos) {
4023                         /* check if URI can be parsed */
4024                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4025                                         (const gchar **)&ep, FALSE)
4026                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4027                                         walk = ep;
4028                         } else
4029                                 walk = scanpos +
4030                                         strlen(parser[last_index].needle);
4031                 } 
4032                 if (bp && ep) {
4033                         uri_start = walk_pos + (bp - o_walk);
4034                         uri_stop  = walk_pos + (ep - o_walk);
4035                 }
4036                 g_free(o_walk);
4037                 o_walk = NULL;
4038                 gtk_text_iter_forward_line(&iter);
4039                 g_free(quote_str);
4040                 quote_str = NULL;
4041                 if (startq_offset != -1) {
4042                         GtkTextIter startquote, endquote;
4043                         gtk_text_buffer_get_iter_at_offset(
4044                                 buffer, &startquote, startq_offset);
4045                         endquote = iter;
4046
4047                         switch (quotelevel) {
4048                         case 0: 
4049                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4050                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4051                                         gtk_text_buffer_apply_tag_by_name(
4052                                                 buffer, "quote0", &startquote, &endquote);
4053                                         gtk_text_buffer_remove_tag_by_name(
4054                                                 buffer, "quote1", &startquote, &endquote);
4055                                         gtk_text_buffer_remove_tag_by_name(
4056                                                 buffer, "quote2", &startquote, &endquote);
4057                                         modified = TRUE;
4058                                 }
4059                                 break;
4060                         case 1: 
4061                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4062                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4063                                         gtk_text_buffer_apply_tag_by_name(
4064                                                 buffer, "quote1", &startquote, &endquote);
4065                                         gtk_text_buffer_remove_tag_by_name(
4066                                                 buffer, "quote0", &startquote, &endquote);
4067                                         gtk_text_buffer_remove_tag_by_name(
4068                                                 buffer, "quote2", &startquote, &endquote);
4069                                         modified = TRUE;
4070                                 }
4071                                 break;
4072                         case 2: 
4073                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4074                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4075                                         gtk_text_buffer_apply_tag_by_name(
4076                                                 buffer, "quote2", &startquote, &endquote);
4077                                         gtk_text_buffer_remove_tag_by_name(
4078                                                 buffer, "quote0", &startquote, &endquote);
4079                                         gtk_text_buffer_remove_tag_by_name(
4080                                                 buffer, "quote1", &startquote, &endquote);
4081                                         modified = TRUE;
4082                                 }
4083                                 break;
4084                         }
4085                         startq_offset = -1;
4086                 } else if (noq_offset != -1) {
4087                         GtkTextIter startnoquote, endnoquote;
4088                         gtk_text_buffer_get_iter_at_offset(
4089                                 buffer, &startnoquote, noq_offset);
4090                         endnoquote = iter;
4091
4092                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4093                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4094                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4095                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4096                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4097                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4098                                 gtk_text_buffer_remove_tag_by_name(
4099                                         buffer, "quote0", &startnoquote, &endnoquote);
4100                                 gtk_text_buffer_remove_tag_by_name(
4101                                         buffer, "quote1", &startnoquote, &endnoquote);
4102                                 gtk_text_buffer_remove_tag_by_name(
4103                                         buffer, "quote2", &startnoquote, &endnoquote);
4104                                 modified = TRUE;
4105                         }
4106                         noq_offset = -1;
4107                 }
4108                 
4109                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4110                         GtkTextIter nouri_start_iter, nouri_end_iter;
4111                         gtk_text_buffer_get_iter_at_offset(
4112                                 buffer, &nouri_start_iter, nouri_start);
4113                         gtk_text_buffer_get_iter_at_offset(
4114                                 buffer, &nouri_end_iter, nouri_stop);
4115                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4116                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4117                                 gtk_text_buffer_remove_tag_by_name(
4118                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4119                                 modified_before_remove = modified;
4120                                 modified = TRUE;
4121                                 removed = TRUE;
4122                         }
4123                 }
4124                 if (uri_start > 0 && uri_stop > 0) {
4125                         GtkTextIter uri_start_iter, uri_end_iter, back;
4126                         gtk_text_buffer_get_iter_at_offset(
4127                                 buffer, &uri_start_iter, uri_start);
4128                         gtk_text_buffer_get_iter_at_offset(
4129                                 buffer, &uri_end_iter, uri_stop);
4130                         back = uri_end_iter;
4131                         gtk_text_iter_backward_char(&back);
4132                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4133                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4134                                 gtk_text_buffer_apply_tag_by_name(
4135                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4136                                 modified = TRUE;
4137                                 if (removed && !modified_before_remove) {
4138                                         modified = FALSE;
4139                                 } 
4140                         }
4141                 }
4142                 if (!modified) {
4143                         debug_print("not modified, out after %d lines\n", lines);
4144                         goto end;
4145                 }
4146         }
4147
4148         debug_print("modified, out after %d lines\n", lines);
4149 end:
4150         if (par_iter)
4151                 *par_iter = iter;
4152         undo_wrapping(compose->undostruct, FALSE);
4153         compose->autowrap = prev_autowrap;
4154         
4155         return modified;
4156 }
4157
4158 void compose_action_cb(void *data)
4159 {
4160         Compose *compose = (Compose *)data;
4161         compose_wrap_all(compose);
4162 }
4163
4164 static void compose_wrap_all(Compose *compose)
4165 {
4166         compose_wrap_all_full(compose, FALSE);
4167 }
4168
4169 static void compose_wrap_all_full(Compose *compose, gboolean force)
4170 {
4171         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4172         GtkTextBuffer *buffer;
4173         GtkTextIter iter;
4174         gboolean modified = TRUE;
4175
4176         buffer = gtk_text_view_get_buffer(text);
4177
4178         gtk_text_buffer_get_start_iter(buffer, &iter);
4179         while (!gtk_text_iter_is_end(&iter) && modified)
4180                 modified = compose_beautify_paragraph(compose, &iter, force);
4181
4182 }
4183
4184 static void compose_set_title(Compose *compose)
4185 {
4186         gchar *str;
4187         gchar *edited;
4188         gchar *subject;
4189         
4190         edited = compose->modified ? _(" [Edited]") : "";
4191         
4192         subject = gtk_editable_get_chars(
4193                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4194
4195 #ifndef MAEMO
4196         if (subject && strlen(subject))
4197                 str = g_strdup_printf(_("%s - Compose message%s"),
4198                                       subject, edited); 
4199         else
4200                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4201 #else
4202         str = g_strdup(_("Compose message"));
4203 #endif
4204
4205         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4206         g_free(str);
4207         g_free(subject);
4208 }
4209
4210 /**
4211  * compose_current_mail_account:
4212  * 
4213  * Find a current mail account (the currently selected account, or the
4214  * default account, if a news account is currently selected).  If a
4215  * mail account cannot be found, display an error message.
4216  * 
4217  * Return value: Mail account, or NULL if not found.
4218  **/
4219 static PrefsAccount *
4220 compose_current_mail_account(void)
4221 {
4222         PrefsAccount *ac;
4223
4224         if (cur_account && cur_account->protocol != A_NNTP)
4225                 ac = cur_account;
4226         else {
4227                 ac = account_get_default();
4228                 if (!ac || ac->protocol == A_NNTP) {
4229                         alertpanel_error(_("Account for sending mail is not specified.\n"
4230                                            "Please select a mail account before sending."));
4231                         return NULL;
4232                 }
4233         }
4234         return ac;
4235 }
4236
4237 #define QUOTE_IF_REQUIRED(out, str)                                     \
4238 {                                                                       \
4239         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4240                 gchar *__tmp;                                           \
4241                 gint len;                                               \
4242                                                                         \
4243                 len = strlen(str) + 3;                                  \
4244                 if ((__tmp = alloca(len)) == NULL) {                    \
4245                         g_warning("can't allocate memory\n");           \
4246                         g_string_free(header, TRUE);                    \
4247                         return NULL;                                    \
4248                 }                                                       \
4249                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4250                 out = __tmp;                                            \
4251         } else {                                                        \
4252                 gchar *__tmp;                                           \
4253                                                                         \
4254                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4255                         g_warning("can't allocate memory\n");           \
4256                         g_string_free(header, TRUE);                    \
4257                         return NULL;                                    \
4258                 } else                                                  \
4259                         strcpy(__tmp, str);                             \
4260                                                                         \
4261                 out = __tmp;                                            \
4262         }                                                               \
4263 }
4264
4265 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4266 {                                                                       \
4267         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4268                 gchar *__tmp;                                           \
4269                 gint len;                                               \
4270                                                                         \
4271                 len = strlen(str) + 3;                                  \
4272                 if ((__tmp = alloca(len)) == NULL) {                    \
4273                         g_warning("can't allocate memory\n");           \
4274                         errret;                                         \
4275                 }                                                       \
4276                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4277                 out = __tmp;                                            \
4278         } else {                                                        \
4279                 gchar *__tmp;                                           \
4280                                                                         \
4281                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4282                         g_warning("can't allocate memory\n");           \
4283                         errret;                                         \
4284                 } else                                                  \
4285                         strcpy(__tmp, str);                             \
4286                                                                         \
4287                 out = __tmp;                                            \
4288         }                                                               \
4289 }
4290
4291 static void compose_select_account(Compose *compose, PrefsAccount *account,
4292                                    gboolean init)
4293 {
4294         GtkItemFactory *ifactory;
4295         gchar *from = NULL;
4296
4297         g_return_if_fail(account != NULL);
4298
4299         compose->account = account;
4300
4301         if (account->name && *account->name) {
4302                 gchar *buf;
4303                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4304                 from = g_strdup_printf("%s <%s>",
4305                                        buf, account->address);
4306                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4307         } else {
4308                 from = g_strdup_printf("<%s>",
4309                                        account->address);
4310                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4311         }
4312
4313         g_free(from);
4314
4315         compose_set_title(compose);
4316
4317         ifactory = gtk_item_factory_from_widget(compose->menubar);
4318
4319         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4320                 menu_set_active(ifactory, "/Options/Sign", TRUE);
4321         else
4322                 menu_set_active(ifactory, "/Options/Sign", FALSE);
4323         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4324                 menu_set_active(ifactory, "/Options/Encrypt", TRUE);
4325         else
4326                 menu_set_active(ifactory, "/Options/Encrypt", FALSE);
4327                                        
4328         activate_privacy_system(compose, account, FALSE);
4329
4330         if (!init && compose->mode != COMPOSE_REDIRECT) {
4331                 undo_block(compose->undostruct);
4332                 compose_insert_sig(compose, TRUE);
4333                 undo_unblock(compose->undostruct);
4334         }
4335
4336 #ifdef USE_ASPELL
4337         /* use account's dict info if set */
4338         if (compose->gtkaspell) {
4339                 if (account->enable_default_dictionary)
4340                         gtkaspell_change_dict(compose->gtkaspell,
4341                                         account->default_dictionary, FALSE);
4342                 if (account->enable_default_alt_dictionary)
4343                         gtkaspell_change_alt_dict(compose->gtkaspell,
4344                                         account->default_alt_dictionary);
4345                 if (account->enable_default_dictionary
4346                         || account->enable_default_alt_dictionary)
4347                         compose_spell_menu_changed(compose);
4348         }
4349 #endif
4350 }
4351
4352 gboolean compose_check_for_valid_recipient(Compose *compose) {
4353         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4354         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4355         gboolean recipient_found = FALSE;
4356         GSList *list;
4357         gchar **strptr;
4358
4359         /* free to and newsgroup list */
4360         slist_free_strings(compose->to_list);
4361         g_slist_free(compose->to_list);
4362         compose->to_list = NULL;
4363                         
4364         slist_free_strings(compose->newsgroup_list);
4365         g_slist_free(compose->newsgroup_list);
4366         compose->newsgroup_list = NULL;
4367
4368         /* search header entries for to and newsgroup entries */
4369         for (list = compose->header_list; list; list = list->next) {
4370                 gchar *header;
4371                 gchar *entry;
4372                 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
4373                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4374
4375                 g_strstrip(entry);
4376                 if (entry[0] != '\0') {
4377                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4378                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4379                                         compose->to_list = address_list_append(compose->to_list, entry);
4380                                         recipient_found = TRUE;
4381                                 }
4382                         }
4383                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4384                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4385                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4386                                         recipient_found = TRUE;
4387                                 }
4388                         }
4389                 }
4390                 g_free(header);
4391                 g_free(entry);
4392         }
4393         return recipient_found;
4394 }
4395
4396 static gboolean compose_check_for_set_recipients(Compose *compose)
4397 {
4398         if (compose->account->set_autocc && compose->account->auto_cc) {
4399                 gboolean found_other = FALSE;
4400                 GSList *list;
4401                 /* search header entries for to and newsgroup entries */
4402                 for (list = compose->header_list; list; list = list->next) {
4403                         gchar *entry;
4404                         gchar *header;
4405                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4406                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
4407                         g_strstrip(entry);
4408                         if (strcmp(entry, compose->account->auto_cc)
4409                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4410                                 found_other = TRUE;
4411                                 g_free(entry);
4412                                 break;
4413                         }
4414                         g_free(entry);
4415                         g_free(header);
4416                 }
4417                 if (!found_other) {
4418                         AlertValue aval;
4419                         if (compose->batch) {
4420                                 gtk_widget_show_all(compose->window);
4421                         }
4422                         aval = alertpanel(_("Send"),
4423                                           _("The only recipient is the default CC address. Send anyway?"),
4424                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4425                         if (aval != G_ALERTALTERNATE)
4426                                 return FALSE;
4427                 }
4428         }
4429         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4430                 gboolean found_other = FALSE;
4431                 GSList *list;
4432                 /* search header entries for to and newsgroup entries */
4433                 for (list = compose->header_list; list; list = list->next) {
4434                         gchar *entry;
4435                         gchar *header;
4436                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4437                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
4438                         g_strstrip(entry);
4439                         if (strcmp(entry, compose->account->auto_bcc)
4440                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4441                                 found_other = TRUE;
4442                                 g_free(entry);
4443                                 break;
4444                         }
4445                         g_free(entry);
4446                         g_free(header);
4447                 }
4448                 if (!found_other) {
4449                         AlertValue aval;
4450                         if (compose->batch) {
4451                                 gtk_widget_show_all(compose->window);
4452                         }
4453                         aval = alertpanel(_("Send"),
4454                                           _("The only recipient is the default BCC address. Send anyway?"),
4455                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4456                         if (aval != G_ALERTALTERNATE)
4457                                 return FALSE;
4458                 }
4459         }
4460         return TRUE;
4461 }
4462
4463 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4464 {
4465         const gchar *str;
4466
4467         if (compose_check_for_valid_recipient(compose) == FALSE) {
4468                 if (compose->batch) {
4469                         gtk_widget_show_all(compose->window);
4470                 }
4471                 alertpanel_error(_("Recipient is not specified."));
4472                 return FALSE;
4473         }
4474
4475         if (compose_check_for_set_recipients(compose) == FALSE) {
4476                 return FALSE;
4477         }
4478
4479         if (!compose->batch) {
4480                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4481                 if (*str == '\0' && check_everything == TRUE && 
4482                     compose->mode != COMPOSE_REDIRECT) {
4483                         AlertValue aval;
4484
4485                         aval = alertpanel(_("Send"),
4486                                           _("Subject is empty. Send it anyway?"),
4487                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4488                         if (aval != G_ALERTALTERNATE)
4489                                 return FALSE;
4490                 }
4491         }
4492
4493         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4494                 return FALSE;
4495
4496         return TRUE;
4497 }
4498
4499 gint compose_send(Compose *compose)
4500 {
4501         gint msgnum;
4502         FolderItem *folder = NULL;
4503         gint val = -1;
4504         gchar *msgpath = NULL;
4505         gboolean discard_window = FALSE;
4506         gchar *errstr = NULL;
4507         gchar *tmsgid = NULL;
4508         MainWindow *mainwin = mainwindow_get_mainwindow();
4509         gboolean queued_removed = FALSE;
4510
4511         if (prefs_common.send_dialog_mode != SEND_DIALOG_ALWAYS
4512                         || compose->batch == TRUE)
4513                 discard_window = TRUE;
4514
4515         compose_allow_user_actions (compose, FALSE);
4516         compose->sending = TRUE;
4517
4518         if (compose_check_entries(compose, TRUE) == FALSE) {
4519                 if (compose->batch) {
4520                         gtk_widget_show_all(compose->window);
4521                 }
4522                 goto bail;
4523         }
4524
4525         inc_lock();
4526         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4527
4528         if (val) {
4529                 if (compose->batch) {
4530                         gtk_widget_show_all(compose->window);
4531                 }
4532                 if (val == -4) {
4533                         alertpanel_error(_("Could not queue message for sending:\n\n"
4534                                            "Charset conversion failed."));
4535                 } else if (val == -5) {
4536                         alertpanel_error(_("Could not queue message for sending:\n\n"
4537                                            "Couldn't get recipient encryption key."));
4538                 } else if (val == -6) {
4539                         /* silent error */
4540                 } else if (val == -3) {
4541                         if (privacy_peek_error())
4542                         alertpanel_error(_("Could not queue message for sending:\n\n"
4543                                            "Signature failed: %s"), privacy_get_error());
4544                 } else if (val == -2 && errno != 0) {
4545                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4546                 } else {
4547                         alertpanel_error(_("Could not queue message for sending."));
4548                 }
4549                 goto bail;
4550         }
4551
4552         tmsgid = g_strdup(compose->msgid);
4553         if (discard_window) {
4554                 compose->sending = FALSE;
4555                 compose_close(compose);
4556                 /* No more compose access in the normal codepath 
4557                  * after this point! */
4558                 compose = NULL;
4559         }
4560
4561         if (msgnum == 0) {
4562                 alertpanel_error(_("The message was queued but could not be "
4563                                    "sent.\nUse \"Send queued messages\" from "
4564                                    "the main window to retry."));
4565                 if (!discard_window) {
4566                         goto bail;
4567                 }
4568                 inc_unlock();
4569                 g_free(tmsgid);
4570                 return -1;
4571         }
4572         if (msgpath == NULL) {
4573                 msgpath = folder_item_fetch_msg(folder, msgnum);
4574                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4575                 g_free(msgpath);
4576         } else {
4577                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4578                 g_unlink(msgpath);
4579                 g_free(msgpath);
4580         }
4581         if (!discard_window) {
4582                 if (val != 0) {
4583                         if (!queued_removed)
4584                                 folder_item_remove_msg(folder, msgnum);
4585                         folder_item_scan(folder);
4586                         if (tmsgid) {
4587                                 /* make sure we delete that */
4588                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4589                                 if (tmp) {
4590                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4591                                         folder_item_remove_msg(folder, tmp->msgnum);
4592                                         procmsg_msginfo_free(tmp);
4593                                 } 
4594                         }
4595                 }
4596         }
4597
4598         if (val == 0) {
4599                 if (!queued_removed)
4600                         folder_item_remove_msg(folder, msgnum);
4601                 folder_item_scan(folder);
4602                 if (tmsgid) {
4603                         /* make sure we delete that */
4604                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4605                         if (tmp) {
4606                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4607                                 folder_item_remove_msg(folder, tmp->msgnum);
4608                                 procmsg_msginfo_free(tmp);
4609                         }
4610                 }
4611                 if (!discard_window) {
4612                         compose->sending = FALSE;
4613                         compose_allow_user_actions (compose, TRUE);
4614                         compose_close(compose);
4615                 }
4616         } else {
4617                 if (errstr) {
4618                         gchar *tmp = g_strdup_printf(_("%s\nUse \"Send queued messages\" from "
4619                                    "the main window to retry."), errstr);
4620                         g_free(errstr);
4621                         alertpanel_error_log(tmp);
4622                         g_free(tmp);
4623                 } else {
4624                         alertpanel_error_log(_("The message was queued but could not be "
4625                                    "sent.\nUse \"Send queued messages\" from "
4626                                    "the main window to retry."));
4627                 }
4628                 if (!discard_window) {
4629                         goto bail;              
4630                 }
4631                 inc_unlock();
4632                 g_free(tmsgid);
4633                 return -1;
4634         }
4635         g_free(tmsgid);
4636         inc_unlock();
4637         toolbar_main_set_sensitive(mainwin);
4638         main_window_set_menu_sensitive(mainwin);
4639         return 0;
4640
4641 bail:
4642         inc_unlock();
4643         g_free(tmsgid);
4644         compose_allow_user_actions (compose, TRUE);
4645         compose->sending = FALSE;
4646         compose->modified = TRUE; 
4647         toolbar_main_set_sensitive(mainwin);
4648         main_window_set_menu_sensitive(mainwin);
4649
4650         return -1;
4651 }
4652
4653 static gboolean compose_use_attach(Compose *compose) 
4654 {
4655         GtkTreeModel *model = gtk_tree_view_get_model
4656                                 (GTK_TREE_VIEW(compose->attach_clist));
4657         return gtk_tree_model_iter_n_children(model, NULL) > 0;
4658 }
4659
4660 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
4661                                                            FILE *fp)
4662 {
4663         gchar buf[BUFFSIZE];
4664         gchar *str;
4665         gboolean first_to_address;
4666         gboolean first_cc_address;
4667         GSList *list;
4668         ComposeHeaderEntry *headerentry;
4669         const gchar *headerentryname;
4670         const gchar *cc_hdr;
4671         const gchar *to_hdr;
4672
4673         debug_print("Writing redirect header\n");
4674
4675         cc_hdr = prefs_common_translated_header_name("Cc:");
4676         to_hdr = prefs_common_translated_header_name("To:");
4677
4678         first_to_address = TRUE;
4679         for (list = compose->header_list; list; list = list->next) {
4680                 headerentry = ((ComposeHeaderEntry *)list->data);
4681                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
4682
4683                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4684                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4685                         Xstrdup_a(str, entstr, return -1);
4686                         g_strstrip(str);
4687                         if (str[0] != '\0') {
4688                                 compose_convert_header
4689                                         (compose, buf, sizeof(buf), str,
4690                                         strlen("Resent-To") + 2, TRUE);
4691
4692                                 if (first_to_address) {
4693                                         fprintf(fp, "Resent-To: ");
4694                                         first_to_address = FALSE;
4695                                 } else {
4696                                         fprintf(fp, ",");
4697                                 }
4698                                 fprintf(fp, "%s", buf);
4699                         }
4700                 }
4701         }
4702         if (!first_to_address) {
4703                 fprintf(fp, "\n");
4704         }
4705
4706         first_cc_address = TRUE;
4707         for (list = compose->header_list; list; list = list->next) {
4708                 headerentry = ((ComposeHeaderEntry *)list->data);
4709                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
4710
4711                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4712                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4713                         Xstrdup_a(str, strg, return -1);
4714                         g_strstrip(str);
4715                         if (str[0] != '\0') {
4716                                 compose_convert_header
4717                                         (compose, buf, sizeof(buf), str,
4718                                         strlen("Resent-Cc") + 2, TRUE);
4719
4720                                 if (first_cc_address) {
4721                                         fprintf(fp, "Resent-Cc: ");
4722                                         first_cc_address = FALSE;
4723                                 } else {
4724                                         fprintf(fp, ",");
4725                                 }
4726                                 fprintf(fp, "%s", buf);
4727                         }
4728                 }
4729         }
4730         if (!first_cc_address) {
4731                 fprintf(fp, "\n");
4732         }
4733         
4734         return(0);
4735 }
4736
4737 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4738 {
4739         gchar buf[BUFFSIZE];
4740         gchar *str;
4741         const gchar *entstr;
4742         /* struct utsname utsbuf; */
4743
4744         g_return_val_if_fail(fp != NULL, -1);
4745         g_return_val_if_fail(compose->account != NULL, -1);
4746         g_return_val_if_fail(compose->account->address != NULL, -1);
4747
4748         /* Resent-Date */
4749         get_rfc822_date(buf, sizeof(buf));
4750         fprintf(fp, "Resent-Date: %s\n", buf);
4751
4752         /* Resent-From */
4753         if (compose->account->name && *compose->account->name) {
4754                 compose_convert_header
4755                         (compose, buf, sizeof(buf), compose->account->name,
4756                          strlen("From: "), TRUE);
4757                 fprintf(fp, "Resent-From: %s <%s>\n",
4758                         buf, compose->account->address);
4759         } else
4760                 fprintf(fp, "Resent-From: %s\n", compose->account->address);
4761
4762         /* Subject */
4763         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4764         if (*entstr != '\0') {
4765                 Xstrdup_a(str, entstr, return -1);
4766                 g_strstrip(str);
4767                 if (*str != '\0') {
4768                         compose_convert_header(compose, buf, sizeof(buf), str,
4769                                                strlen("Subject: "), FALSE);
4770                         fprintf(fp, "Subject: %s\n", buf);
4771                 }
4772         }
4773
4774         /* Resent-Message-ID */
4775         if (compose->account->gen_msgid) {
4776                 generate_msgid(buf, sizeof(buf));
4777                 fprintf(fp, "Resent-Message-ID: <%s>\n", buf);
4778                 compose->msgid = g_strdup(buf);
4779         }
4780
4781         compose_redirect_write_headers_from_headerlist(compose, fp);
4782
4783         /* separator between header and body */
4784         fputs("\n", fp);
4785
4786         return 0;
4787 }
4788
4789 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4790 {
4791         FILE *fp;
4792         size_t len;
4793         gchar buf[BUFFSIZE];
4794         int i = 0;
4795         gboolean skip = FALSE;
4796         gchar *not_included[]={
4797                 "Return-Path:",         "Delivered-To:",        "Received:",
4798                 "Subject:",             "X-UIDL:",              "AF:",
4799                 "NF:",                  "PS:",                  "SRH:",
4800                 "SFN:",                 "DSR:",                 "MID:",
4801                 "CFG:",                 "PT:",                  "S:",
4802                 "RQ:",                  "SSV:",                 "NSV:",
4803                 "SSH:",                 "R:",                   "MAID:",
4804                 "NAID:",                "RMID:",                "FMID:",
4805                 "SCF:",                 "RRCPT:",               "NG:",
4806                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
4807                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
4808                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
4809                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
4810                 NULL
4811                 };
4812         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
4813                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
4814                 return -1;
4815         }
4816
4817         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
4818                 skip = FALSE;
4819                 for (i = 0; not_included[i] != NULL; i++) {
4820                         if (g_ascii_strncasecmp(buf, not_included[i],
4821                                                 strlen(not_included[i])) == 0) {
4822                                 skip = TRUE;
4823                                 break;
4824                         }
4825                 }
4826                 if (skip)
4827                         continue;
4828                 if (fputs(buf, fdest) == -1)
4829                         goto error;
4830
4831                 if (!prefs_common.redirect_keep_from) {
4832                         if (g_ascii_strncasecmp(buf, "From:",
4833                                           strlen("From:")) == 0) {
4834                                 fputs(" (by way of ", fdest);
4835                                 if (compose->account->name
4836                                     && *compose->account->name) {
4837                                         compose_convert_header
4838                                                 (compose, buf, sizeof(buf),
4839                                                  compose->account->name,
4840                                                  strlen("From: "),
4841                                                  FALSE);
4842                                         fprintf(fdest, "%s <%s>",
4843                                                 buf,
4844                                                 compose->account->address);
4845                                 } else
4846                                         fprintf(fdest, "%s",
4847                                                 compose->account->address);
4848                                 fputs(")", fdest);
4849                         }
4850                 }
4851
4852                 if (fputs("\n", fdest) == -1)
4853                         goto error;
4854         }
4855
4856         compose_redirect_write_headers(compose, fdest);
4857
4858         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4859                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
4860                         goto error;
4861         }
4862
4863         fclose(fp);
4864
4865         return 0;
4866 error:
4867         fclose(fp);
4868
4869         return -1;
4870 }
4871
4872 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
4873 {
4874         GtkTextBuffer *buffer;
4875         GtkTextIter start, end;
4876         gchar *chars;
4877         gchar *buf;
4878         const gchar *out_codeset;
4879         EncodingType encoding;
4880         MimeInfo *mimemsg, *mimetext;
4881         gint line;
4882
4883         if (action == COMPOSE_WRITE_FOR_SEND)
4884                 attach_parts = TRUE;
4885
4886         /* create message MimeInfo */
4887         mimemsg = procmime_mimeinfo_new();
4888         mimemsg->type = MIMETYPE_MESSAGE;
4889         mimemsg->subtype = g_strdup("rfc822");
4890         mimemsg->content = MIMECONTENT_MEM;
4891         mimemsg->tmp = TRUE; /* must free content later */
4892         mimemsg->data.mem = compose_get_header(compose);
4893
4894         /* Create text part MimeInfo */
4895         /* get all composed text */
4896         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4897         gtk_text_buffer_get_start_iter(buffer, &start);
4898         gtk_text_buffer_get_end_iter(buffer, &end);
4899         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4900         if (is_ascii_str(chars)) {
4901                 buf = chars;
4902                 chars = NULL;
4903                 out_codeset = CS_US_ASCII;
4904                 encoding = ENC_7BIT;
4905         } else {
4906                 const gchar *src_codeset = CS_INTERNAL;
4907
4908                 out_codeset = conv_get_charset_str(compose->out_encoding);
4909
4910                 if (!out_codeset) {
4911                         gchar *test_conv_global_out = NULL;
4912                         gchar *test_conv_reply = NULL;
4913
4914                         /* automatic mode. be automatic. */
4915                         codeconv_set_strict(TRUE);
4916                         
4917                         out_codeset = conv_get_outgoing_charset_str();
4918                         if (out_codeset) {
4919                                 debug_print("trying to convert to %s\n", out_codeset);
4920                                 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
4921                         }
4922                         
4923                         if (!test_conv_global_out && compose->orig_charset
4924                         &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
4925                                 out_codeset = compose->orig_charset;
4926                                 debug_print("failure; trying to convert to %s\n", out_codeset);
4927                                 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
4928                         }
4929                         
4930                         if (!test_conv_global_out && !test_conv_reply) {
4931                                 /* we're lost */
4932                                 out_codeset = CS_INTERNAL;
4933                                 debug_print("failure; finally using %s\n", out_codeset);
4934                         }
4935                         g_free(test_conv_global_out);
4936                         g_free(test_conv_reply);
4937                         codeconv_set_strict(FALSE);
4938                 }
4939
4940                 if (!g_ascii_strcasecmp(out_codeset, CS_US_ASCII))
4941                         out_codeset = CS_ISO_8859_1;
4942
4943                 if (prefs_common.encoding_method == CTE_BASE64)
4944                         encoding = ENC_BASE64;
4945                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
4946                         encoding = ENC_QUOTED_PRINTABLE;
4947                 else if (prefs_common.encoding_method == CTE_8BIT)
4948                         encoding = ENC_8BIT;
4949                 else
4950                         encoding = procmime_get_encoding_for_charset(out_codeset);
4951
4952                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
4953                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
4954
4955                 if (action == COMPOSE_WRITE_FOR_SEND) {
4956                         codeconv_set_strict(TRUE);
4957                         buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
4958                         codeconv_set_strict(FALSE);
4959
4960                         if (!buf) {
4961                                 AlertValue aval;
4962                                 gchar *msg;
4963
4964                                 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
4965                                                         "to the specified %s charset.\n"
4966                                                         "Send it as %s?"), out_codeset, src_codeset);
4967                                 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
4968                                                       NULL, ALERT_ERROR, G_ALERTDEFAULT);
4969                                 g_free(msg);
4970
4971                                 if (aval != G_ALERTALTERNATE) {
4972                                         g_free(chars);
4973                                         return -3;
4974                                 } else {
4975                                         buf = chars;
4976                                         out_codeset = src_codeset;
4977                                         chars = NULL;
4978                                 }
4979                         }
4980                 } else {
4981                         buf = chars;
4982                         out_codeset = src_codeset;
4983                         chars = NULL;
4984                 }
4985         }
4986         g_free(chars);
4987
4988         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
4989                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
4990                     strstr(buf, "\nFrom ") != NULL) {
4991                         encoding = ENC_QUOTED_PRINTABLE;
4992                 }
4993         }
4994
4995         mimetext = procmime_mimeinfo_new();
4996         mimetext->content = MIMECONTENT_MEM;
4997         mimetext->tmp = TRUE; /* must free content later */
4998         /* dup'ed because procmime_encode_content can turn it into a tmpfile
4999          * and free the data, which we need later. */
5000         mimetext->data.mem = g_strdup(buf); 
5001         mimetext->type = MIMETYPE_TEXT;
5002         mimetext->subtype = g_strdup("plain");
5003         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5004                             g_strdup(out_codeset));
5005                             
5006         /* protect trailing spaces when signing message */
5007         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5008             privacy_system_can_sign(compose->privacy_system)) {
5009                 encoding = ENC_QUOTED_PRINTABLE;
5010         }
5011         
5012         debug_print("main text: %d bytes encoded as %s in %d\n",
5013                 strlen(buf), out_codeset, encoding);
5014
5015         /* check for line length limit */
5016         if (action == COMPOSE_WRITE_FOR_SEND &&
5017             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5018             check_line_length(buf, 1000, &line) < 0) {
5019                 AlertValue aval;
5020                 gchar *msg;
5021
5022                 msg = g_strdup_printf
5023                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5024                            "The contents of the message might be broken on the way to the delivery.\n"
5025                            "\n"
5026                            "Send it anyway?"), line + 1);
5027                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5028                 g_free(msg);
5029                 if (aval != G_ALERTALTERNATE) {
5030                         g_free(buf);
5031                         return -1;
5032                 }
5033         }
5034         
5035         if (encoding != ENC_UNKNOWN)
5036                 procmime_encode_content(mimetext, encoding);
5037
5038         /* append attachment parts */
5039         if (compose_use_attach(compose) && attach_parts) {
5040                 MimeInfo *mimempart;
5041                 gchar *boundary = NULL;
5042                 mimempart = procmime_mimeinfo_new();
5043                 mimempart->content = MIMECONTENT_EMPTY;
5044                 mimempart->type = MIMETYPE_MULTIPART;
5045                 mimempart->subtype = g_strdup("mixed");
5046
5047                 do {
5048                         g_free(boundary);
5049                         boundary = generate_mime_boundary(NULL);
5050                 } while (strstr(buf, boundary) != NULL);
5051
5052                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5053                                     boundary);
5054
5055                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5056
5057                 g_node_append(mimempart->node, mimetext->node);
5058                 g_node_append(mimemsg->node, mimempart->node);
5059
5060                 compose_add_attachments(compose, mimempart);
5061         } else
5062                 g_node_append(mimemsg->node, mimetext->node);
5063
5064         g_free(buf);
5065
5066         /* sign message if sending */
5067         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5068             privacy_system_can_sign(compose->privacy_system))
5069                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5070                         return -2;
5071
5072         procmime_write_mimeinfo(mimemsg, fp);
5073         
5074         procmime_mimeinfo_free_all(mimemsg);
5075
5076         return 0;
5077 }
5078
5079 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5080 {
5081         GtkTextBuffer *buffer;
5082         GtkTextIter start, end;
5083         FILE *fp;
5084         size_t len;
5085         gchar *chars, *tmp;
5086
5087         if ((fp = g_fopen(file, "wb")) == NULL) {
5088                 FILE_OP_ERROR(file, "fopen");
5089                 return -1;
5090         }
5091
5092         /* chmod for security */
5093         if (change_file_mode_rw(fp, file) < 0) {
5094                 FILE_OP_ERROR(file, "chmod");
5095                 g_warning("can't change file mode\n");
5096         }
5097
5098         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5099         gtk_text_buffer_get_start_iter(buffer, &start);
5100         gtk_text_buffer_get_end_iter(buffer, &end);
5101         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5102
5103         chars = conv_codeset_strdup
5104                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5105
5106         g_free(tmp);
5107         if (!chars) return -1;
5108
5109         /* write body */
5110         len = strlen(chars);
5111         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5112                 FILE_OP_ERROR(file, "fwrite");
5113                 g_free(chars);
5114                 fclose(fp);
5115                 g_unlink(file);
5116                 return -1;
5117         }
5118
5119         g_free(chars);
5120
5121         if (fclose(fp) == EOF) {
5122                 FILE_OP_ERROR(file, "fclose");
5123                 g_unlink(file);
5124                 return -1;
5125         }
5126         return 0;
5127 }
5128
5129 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5130 {
5131         FolderItem *item;
5132         MsgInfo *msginfo = compose->targetinfo;
5133
5134         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5135         if (!msginfo) return -1;
5136
5137         if (!force && MSG_IS_LOCKED(msginfo->flags))
5138                 return 0;
5139
5140         item = msginfo->folder;
5141         g_return_val_if_fail(item != NULL, -1);
5142
5143         if (procmsg_msg_exist(msginfo) &&
5144             (folder_has_parent_of_type(item, F_QUEUE) ||
5145              folder_has_parent_of_type(item, F_DRAFT) 
5146              || msginfo == compose->autosaved_draft)) {
5147                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5148                         g_warning("can't remove the old message\n");
5149                         return -1;
5150                 } else {
5151                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5152                 }
5153         }
5154
5155         return 0;
5156 }
5157
5158 static void compose_remove_draft(Compose *compose)
5159 {
5160         FolderItem *drafts;
5161         MsgInfo *msginfo = compose->targetinfo;
5162         drafts = account_get_special_folder(compose->account, F_DRAFT);
5163
5164         if (procmsg_msg_exist(msginfo)) {
5165                 folder_item_remove_msg(drafts, msginfo->msgnum);
5166         }
5167
5168 }
5169
5170 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5171                    gboolean remove_reedit_target)
5172 {
5173         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5174 }
5175
5176 static gboolean compose_warn_encryption(Compose *compose)
5177 {
5178         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5179         AlertValue val = G_ALERTALTERNATE;
5180         
5181         if (warning == NULL)
5182                 return TRUE;
5183
5184         val = alertpanel_full(_("Encryption warning"), warning,
5185                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5186                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5187         if (val & G_ALERTDISABLE) {
5188                 val &= ~G_ALERTDISABLE;
5189                 if (val == G_ALERTALTERNATE)
5190                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5191                                 TRUE);
5192         }
5193
5194         if (val == G_ALERTALTERNATE) {
5195                 return TRUE;
5196         } else {
5197                 return FALSE;
5198         } 
5199 }
5200
5201 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5202                               gchar **msgpath, gboolean check_subject,
5203                               gboolean remove_reedit_target)
5204 {
5205         FolderItem *queue;
5206         gchar *tmp;
5207         FILE *fp;
5208         GSList *cur;
5209         gint num;
5210         static gboolean lock = FALSE;
5211         PrefsAccount *mailac = NULL, *newsac = NULL;
5212         
5213         debug_print("queueing message...\n");
5214         g_return_val_if_fail(compose->account != NULL, -1);
5215
5216         lock = TRUE;
5217         
5218         if (compose_check_entries(compose, check_subject) == FALSE) {
5219                 lock = FALSE;
5220                 if (compose->batch) {
5221                         gtk_widget_show_all(compose->window);
5222                 }
5223                 return -1;
5224         }
5225
5226         if (!compose->to_list && !compose->newsgroup_list) {
5227                 g_warning("can't get recipient list.");
5228                 lock = FALSE;
5229                 return -1;
5230         }
5231
5232         if (compose->to_list) {
5233                 if (compose->account->protocol != A_NNTP)
5234                         mailac = compose->account;
5235                 else if (cur_account && cur_account->protocol != A_NNTP)
5236                         mailac = cur_account;
5237                 else if (!(mailac = compose_current_mail_account())) {
5238                         lock = FALSE;
5239                         alertpanel_error(_("No account for sending mails available!"));
5240                         return -1;
5241                 }
5242         }
5243
5244         if (compose->newsgroup_list) {
5245                 if (compose->account->protocol == A_NNTP)
5246                         newsac = compose->account;
5247                 else if (!newsac->protocol != A_NNTP) {
5248                         lock = FALSE;
5249                         alertpanel_error(_("No account for posting news available!"));
5250                         return -1;
5251                 }                       
5252         }
5253
5254         /* write queue header */
5255         tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
5256                               G_DIR_SEPARATOR, compose);
5257         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5258                 FILE_OP_ERROR(tmp, "fopen");
5259                 g_free(tmp);
5260                 lock = FALSE;
5261                 return -2;
5262         }
5263
5264         if (change_file_mode_rw(fp, tmp) < 0) {
5265                 FILE_OP_ERROR(tmp, "chmod");
5266                 g_warning("can't change file mode\n");
5267         }
5268
5269         /* queueing variables */
5270         fprintf(fp, "AF:\n");
5271         fprintf(fp, "NF:0\n");
5272         fprintf(fp, "PS:10\n");
5273         fprintf(fp, "SRH:1\n");
5274         fprintf(fp, "SFN:\n");
5275         fprintf(fp, "DSR:\n");
5276         if (compose->msgid)
5277                 fprintf(fp, "MID:<%s>\n", compose->msgid);
5278         else
5279                 fprintf(fp, "MID:\n");
5280         fprintf(fp, "CFG:\n");
5281         fprintf(fp, "PT:0\n");
5282         fprintf(fp, "S:%s\n", compose->account->address);
5283         fprintf(fp, "RQ:\n");
5284         if (mailac)
5285                 fprintf(fp, "SSV:%s\n", mailac->smtp_server);
5286         else
5287                 fprintf(fp, "SSV:\n");
5288         if (newsac)
5289                 fprintf(fp, "NSV:%s\n", newsac->nntp_server);
5290         else
5291                 fprintf(fp, "NSV:\n");
5292         fprintf(fp, "SSH:\n");
5293         /* write recepient list */
5294         if (compose->to_list) {
5295                 fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
5296                 for (cur = compose->to_list->next; cur != NULL;
5297                      cur = cur->next)
5298                         fprintf(fp, ",<%s>", (gchar *)cur->data);
5299                 fprintf(fp, "\n");
5300         }
5301         /* write newsgroup list */
5302         if (compose->newsgroup_list) {
5303                 fprintf(fp, "NG:");
5304                 fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data);
5305                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5306                         fprintf(fp, ",%s", (gchar *)cur->data);
5307                 fprintf(fp, "\n");
5308         }
5309         /* Sylpheed account IDs */
5310         if (mailac)
5311                 fprintf(fp, "MAID:%d\n", mailac->account_id);
5312         if (newsac)
5313                 fprintf(fp, "NAID:%d\n", newsac->account_id);
5314
5315         
5316         if (compose->privacy_system != NULL) {
5317                 fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system);
5318                 fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing);
5319                 if (compose->use_encryption) {
5320                         gchar *encdata;
5321                         if (!compose_warn_encryption(compose)) {
5322                                 lock = FALSE;
5323                                 fclose(fp);
5324                                 g_unlink(tmp);
5325                                 g_free(tmp);
5326                                 return -6;
5327                         }
5328                         if (mailac && mailac->encrypt_to_self) {
5329                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5330                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5331                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5332                                 g_slist_free(tmp_list);
5333                         } else {
5334                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5335                         }
5336                         if (encdata != NULL) {
5337                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5338                                         fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption);
5339                                         fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5340                                                 encdata);
5341                                 } /* else we finally dont want to encrypt */
5342                         } else {
5343                                 fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption);
5344                                 /* and if encdata was null, it means there's been a problem in 
5345                                  * key selection */
5346                                 lock = FALSE;
5347                                 fclose(fp);
5348                                 g_unlink(tmp);
5349                                 g_free(tmp);
5350                                 return -5;
5351                         }
5352                         g_free(encdata);
5353                 }
5354         }
5355
5356         /* Save copy folder */
5357         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5358                 gchar *savefolderid;
5359                 
5360                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5361                 fprintf(fp, "SCF:%s\n", savefolderid);
5362                 g_free(savefolderid);
5363         }
5364         /* Save copy folder */
5365         if (compose->return_receipt) {
5366                 fprintf(fp, "RRCPT:1\n");
5367         }
5368         /* Message-ID of message replying to */
5369         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5370                 gchar *folderid;
5371                 
5372                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5373                 fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid);
5374                 g_free(folderid);
5375         }
5376         /* Message-ID of message forwarding to */
5377         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5378                 gchar *folderid;
5379                 
5380                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5381                 fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid);
5382                 g_free(folderid);
5383         }
5384
5385         /* end of headers */
5386         fprintf(fp, "X-Claws-End-Special-Headers: 1\n");
5387
5388         if (compose->redirect_filename != NULL) {
5389                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5390                         lock = FALSE;
5391                         fclose(fp);
5392                         g_unlink(tmp);
5393                         g_free(tmp);
5394                         return -2;
5395                 }
5396         } else {
5397                 gint result = 0;
5398                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5399                         lock = FALSE;
5400                         fclose(fp);
5401                         g_unlink(tmp);
5402                         g_free(tmp);
5403                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5404                 }
5405         }
5406
5407         if (fclose(fp) == EOF) {
5408                 FILE_OP_ERROR(tmp, "fclose");
5409                 g_unlink(tmp);
5410                 g_free(tmp);
5411                 lock = FALSE;
5412                 return -2;
5413         }
5414
5415         if (item && *item) {
5416                 queue = *item;
5417         } else {
5418                 queue = account_get_special_folder(compose->account, F_QUEUE);
5419         }
5420         if (!queue) {
5421                 g_warning("can't find queue folder\n");
5422                 g_unlink(tmp);
5423                 g_free(tmp);
5424                 lock = FALSE;
5425                 return -1;
5426         }
5427         folder_item_scan(queue);
5428         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5429                 g_warning("can't queue the message\n");
5430                 g_unlink(tmp);
5431                 g_free(tmp);
5432                 lock = FALSE;
5433                 return -1;
5434         }
5435         
5436         if (msgpath == NULL) {
5437                 g_unlink(tmp);
5438                 g_free(tmp);
5439         } else
5440                 *msgpath = tmp;
5441
5442         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5443                 compose_remove_reedit_target(compose, FALSE);
5444         }
5445
5446         if ((msgnum != NULL) && (item != NULL)) {
5447                 *msgnum = num;
5448                 *item = queue;
5449         }
5450
5451         return 0;
5452 }
5453
5454 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5455 {
5456         AttachInfo *ainfo;
5457         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5458         MimeInfo *mimepart;
5459         struct stat statbuf;
5460         gchar *type, *subtype;
5461         GtkTreeModel *model;
5462         GtkTreeIter iter;
5463
5464         model = gtk_tree_view_get_model(tree_view);
5465         
5466         if (!gtk_tree_model_get_iter_first(model, &iter))
5467                 return;
5468         do {
5469                 gtk_tree_model_get(model, &iter,
5470                                    COL_DATA, &ainfo,
5471                                    -1);
5472                                                            
5473                 mimepart = procmime_mimeinfo_new();
5474                 mimepart->content = MIMECONTENT_FILE;
5475                 mimepart->data.filename = g_strdup(ainfo->file);
5476                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5477                 mimepart->offset = 0;
5478
5479                 stat(ainfo->file, &statbuf);
5480                 mimepart->length = statbuf.st_size;
5481
5482                 type = g_strdup(ainfo->content_type);
5483
5484                 if (!strchr(type, '/')) {
5485                         g_free(type);
5486                         type = g_strdup("application/octet-stream");
5487                 }
5488
5489                 subtype = strchr(type, '/') + 1;
5490                 *(subtype - 1) = '\0';
5491                 mimepart->type = procmime_get_media_type(type);
5492                 mimepart->subtype = g_strdup(subtype);
5493                 g_free(type);
5494
5495                 if (mimepart->type == MIMETYPE_MESSAGE && 
5496                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5497                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5498                 } else {
5499                         if (ainfo->name) {
5500                                 g_hash_table_insert(mimepart->typeparameters,
5501                                             g_strdup("name"), g_strdup(ainfo->name));
5502                                 g_hash_table_insert(mimepart->dispositionparameters,
5503                                             g_strdup("filename"), g_strdup(ainfo->name));
5504                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5505                         }
5506                 }
5507
5508                 if (compose->use_signing) {
5509                         if (ainfo->encoding == ENC_7BIT)
5510                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5511                         else if (ainfo->encoding == ENC_8BIT)
5512                                 ainfo->encoding = ENC_BASE64;
5513                 }
5514                 
5515                 procmime_encode_content(mimepart, ainfo->encoding);
5516
5517                 g_node_append(parent->node, mimepart->node);
5518         } while (gtk_tree_model_iter_next(model, &iter));
5519 }
5520
5521 #define IS_IN_CUSTOM_HEADER(header) \
5522         (compose->account->add_customhdr && \
5523          custom_header_find(compose->account->customhdr_list, header) != NULL)
5524
5525 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5526                                                     GString *header, 
5527                                                     const gchar *fieldname,
5528                                                     const gchar *seperator)
5529 {
5530         gchar *str, *fieldname_w_colon;
5531         gboolean add_field = FALSE;
5532         GSList *list;
5533         ComposeHeaderEntry *headerentry;
5534         const gchar *headerentryname;
5535         const gchar *trans_fieldname;
5536         GString *fieldstr;
5537
5538         if (IS_IN_CUSTOM_HEADER(fieldname))
5539                 return;
5540
5541         debug_print("Adding %s-fields\n", fieldname);
5542
5543         fieldstr = g_string_sized_new(64);
5544
5545         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5546         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5547
5548         for (list = compose->header_list; list; list = list->next) {
5549                 headerentry = ((ComposeHeaderEntry *)list->data);
5550                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
5551
5552                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5553                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5554                         g_strstrip(str);
5555                         if (str[0] != '\0') {
5556                                 if (add_field)
5557                                         g_string_append(fieldstr, seperator);
5558                                 g_string_append(fieldstr, str);
5559                                 add_field = TRUE;
5560                         }
5561                         g_free(str);
5562                 }
5563         }
5564         if (add_field) {
5565                 gchar *buf;
5566
5567                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5568                 compose_convert_header
5569                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5570                         strlen(fieldname) + 2, TRUE);
5571                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5572                 g_free(buf);
5573         }
5574
5575         g_free(fieldname_w_colon);
5576         g_string_free(fieldstr, TRUE);
5577
5578         return;
5579 }
5580
5581 static gchar *compose_get_header(Compose *compose)
5582 {
5583         gchar buf[BUFFSIZE];
5584         const gchar *entry_str;
5585         gchar *str;
5586         gchar *name;
5587         GSList *list;
5588         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5589         GString *header;
5590         gchar *from_name = NULL, *from_address = NULL;
5591         gchar *tmp;
5592
5593         g_return_val_if_fail(compose->account != NULL, NULL);
5594         g_return_val_if_fail(compose->account->address != NULL, NULL);
5595
5596         header = g_string_sized_new(64);
5597
5598         /* Date */
5599         get_rfc822_date(buf, sizeof(buf));
5600         g_string_append_printf(header, "Date: %s\n", buf);
5601
5602         /* From */
5603         
5604         if (compose->account->name && *compose->account->name) {
5605                 gchar *buf;
5606                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5607                 tmp = g_strdup_printf("%s <%s>",
5608                         buf, compose->account->address);
5609         } else {
5610                 tmp = g_strdup_printf("%s",
5611                         compose->account->address);
5612         }
5613         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5614         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5615                 /* use default */
5616                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5617                 from_address = g_strdup(compose->account->address);
5618         } else {
5619                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5620                 /* extract name and address */
5621                 if (strstr(spec, " <") && strstr(spec, ">")) {
5622                         from_address = g_strdup(strrchr(spec, '<')+1);
5623                         *(strrchr(from_address, '>')) = '\0';
5624                         from_name = g_strdup(spec);
5625                         *(strrchr(from_name, '<')) = '\0';
5626                 } else {
5627                         from_name = NULL;
5628                         from_address = g_strdup(spec);
5629                 }
5630                 g_free(spec);
5631         }
5632         g_free(tmp);
5633         
5634         
5635         if (from_name && *from_name) {
5636                 compose_convert_header
5637                         (compose, buf, sizeof(buf), from_name,
5638                          strlen("From: "), TRUE);
5639                 QUOTE_IF_REQUIRED(name, buf);
5640                 
5641                 g_string_append_printf(header, "From: %s <%s>\n",
5642                         name, from_address);
5643         } else
5644                 g_string_append_printf(header, "From: %s\n", from_address);
5645         
5646         g_free(from_name);
5647         g_free(from_address);
5648
5649         /* To */
5650         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5651
5652         /* Newsgroups */
5653         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5654
5655         /* Cc */
5656         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5657
5658         /* Bcc */
5659         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5660
5661         /* Subject */
5662         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5663
5664         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5665                 g_strstrip(str);
5666                 if (*str != '\0') {
5667                         compose_convert_header(compose, buf, sizeof(buf), str,
5668                                                strlen("Subject: "), FALSE);
5669                         g_string_append_printf(header, "Subject: %s\n", buf);
5670                 }
5671         }
5672         g_free(str);
5673
5674         /* Message-ID */
5675         if (compose->account->gen_msgid) {
5676                 generate_msgid(buf, sizeof(buf));
5677                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5678                 compose->msgid = g_strdup(buf);
5679         }
5680
5681         if (compose->remove_references == FALSE) {
5682                 /* In-Reply-To */
5683                 if (compose->inreplyto && compose->to_list)
5684                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5685         
5686                 /* References */
5687                 if (compose->references)
5688                         g_string_append_printf(header, "References: %s\n", compose->references);
5689         }
5690
5691         /* Followup-To */
5692         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5693
5694         /* Reply-To */
5695         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5696
5697         /* Organization */
5698         if (compose->account->organization &&
5699             strlen(compose->account->organization) &&
5700             !IS_IN_CUSTOM_HEADER("Organization")) {
5701                 compose_convert_header(compose, buf, sizeof(buf),
5702                                        compose->account->organization,
5703                                        strlen("Organization: "), FALSE);
5704                 g_string_append_printf(header, "Organization: %s\n", buf);
5705         }
5706
5707         /* Program version and system info */
5708         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5709             !compose->newsgroup_list) {
5710                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5711                         prog_version,
5712                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5713                         TARGET_ALIAS);
5714         }
5715         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5716                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5717                         prog_version,
5718                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5719                         TARGET_ALIAS);
5720         }
5721
5722         /* custom headers */
5723         if (compose->account->add_customhdr) {
5724                 GSList *cur;
5725
5726                 for (cur = compose->account->customhdr_list; cur != NULL;
5727                      cur = cur->next) {
5728                         CustomHeader *chdr = (CustomHeader *)cur->data;
5729
5730                         if (custom_header_is_allowed(chdr->name)) {
5731                                 compose_convert_header
5732                                         (compose, buf, sizeof(buf),
5733                                          chdr->value ? chdr->value : "",
5734                                          strlen(chdr->name) + 2, FALSE);
5735                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5736                         }
5737                 }
5738         }
5739
5740         /* PRIORITY */
5741         switch (compose->priority) {
5742                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5743                                                    "X-Priority: 1 (Highest)\n");
5744                         break;
5745                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5746                                                 "X-Priority: 2 (High)\n");
5747                         break;
5748                 case PRIORITY_NORMAL: break;
5749                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5750                                                "X-Priority: 4 (Low)\n");
5751                         break;
5752                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5753                                                   "X-Priority: 5 (Lowest)\n");
5754                         break;
5755                 default: debug_print("compose: priority unknown : %d\n",
5756                                      compose->priority);
5757         }
5758
5759         /* Request Return Receipt */
5760         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5761                 if (compose->return_receipt) {
5762                         if (compose->account->name
5763                             && *compose->account->name) {
5764                                 compose_convert_header(compose, buf, sizeof(buf), 
5765                                                        compose->account->name, 
5766                                                        strlen("Disposition-Notification-To: "),
5767                                                        TRUE);
5768                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
5769                         } else
5770                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
5771                 }
5772         }
5773
5774         /* get special headers */
5775         for (list = compose->header_list; list; list = list->next) {
5776                 ComposeHeaderEntry *headerentry;
5777                 gchar *tmp;
5778                 gchar *headername;
5779                 gchar *headername_wcolon;
5780                 const gchar *headername_trans;
5781                 gchar *headervalue;
5782                 gchar **string;
5783                 gboolean standard_header = FALSE;
5784
5785                 headerentry = ((ComposeHeaderEntry *)list->data);
5786                 
5787                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry)));
5788                 if (strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
5789                         g_free(tmp);
5790                         continue;
5791                 }
5792
5793                 if (!strstr(tmp, ":")) {
5794                         headername_wcolon = g_strconcat(tmp, ":", NULL);
5795                         headername = g_strdup(tmp);
5796                 } else {
5797                         headername_wcolon = g_strdup(tmp);
5798                         headername = g_strdup(strtok(tmp, ":"));
5799                 }
5800                 g_free(tmp);
5801                 
5802                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5803                 Xstrdup_a(headervalue, entry_str, return NULL);
5804                 subst_char(headervalue, '\r', ' ');
5805                 subst_char(headervalue, '\n', ' ');
5806                 string = std_headers;
5807                 while (*string != NULL) {
5808                         headername_trans = prefs_common_translated_header_name(*string);
5809                         if (!strcmp(headername_trans, headername_wcolon))
5810                                 standard_header = TRUE;
5811                         string++;
5812                 }
5813                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
5814                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
5815                                 
5816                 g_free(headername);
5817                 g_free(headername_wcolon);              
5818         }
5819
5820         str = header->str;
5821         g_string_free(header, FALSE);
5822
5823         return str;
5824 }
5825
5826 #undef IS_IN_CUSTOM_HEADER
5827
5828 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
5829                                    gint header_len, gboolean addr_field)
5830 {
5831         gchar *tmpstr = NULL;
5832         const gchar *out_codeset = NULL;
5833
5834         g_return_if_fail(src != NULL);
5835         g_return_if_fail(dest != NULL);
5836
5837         if (len < 1) return;
5838
5839         tmpstr = g_strdup(src);
5840
5841         subst_char(tmpstr, '\n', ' ');
5842         subst_char(tmpstr, '\r', ' ');
5843         g_strchomp(tmpstr);
5844
5845         if (!g_utf8_validate(tmpstr, -1, NULL)) {
5846                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
5847                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
5848                 g_free(tmpstr);
5849                 tmpstr = mybuf;
5850         }
5851
5852         codeconv_set_strict(TRUE);
5853         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5854                 conv_get_charset_str(compose->out_encoding));
5855         codeconv_set_strict(FALSE);
5856         
5857         if (!dest || *dest == '\0') {
5858                 gchar *test_conv_global_out = NULL;
5859                 gchar *test_conv_reply = NULL;
5860
5861                 /* automatic mode. be automatic. */
5862                 codeconv_set_strict(TRUE);
5863
5864                 out_codeset = conv_get_outgoing_charset_str();
5865                 if (out_codeset) {
5866                         debug_print("trying to convert to %s\n", out_codeset);
5867                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5868                 }
5869
5870                 if (!test_conv_global_out && compose->orig_charset
5871                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5872                         out_codeset = compose->orig_charset;
5873                         debug_print("failure; trying to convert to %s\n", out_codeset);
5874                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5875                 }
5876
5877                 if (!test_conv_global_out && !test_conv_reply) {
5878                         /* we're lost */
5879                         out_codeset = CS_INTERNAL;
5880                         debug_print("finally using %s\n", out_codeset);
5881                 }
5882                 g_free(test_conv_global_out);
5883                 g_free(test_conv_reply);
5884                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5885                                         out_codeset);
5886                 codeconv_set_strict(FALSE);
5887         }
5888         g_free(tmpstr);
5889 }
5890
5891 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
5892 {
5893         gchar *address;
5894
5895         g_return_if_fail(user_data != NULL);
5896
5897         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
5898         g_strstrip(address);
5899         if (*address != '\0') {
5900                 gchar *name = procheader_get_fromname(address);
5901                 extract_address(address);
5902                 addressbook_add_contact(name, address, NULL);
5903         }
5904         g_free(address);
5905 }
5906
5907 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
5908 {
5909         GtkWidget *menuitem;
5910         gchar *address;
5911
5912         g_return_if_fail(menu != NULL);
5913         g_return_if_fail(GTK_IS_MENU_SHELL(menu));
5914
5915         menuitem = gtk_separator_menu_item_new();
5916         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
5917         gtk_widget_show(menuitem);
5918
5919         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
5920         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
5921
5922         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
5923         g_strstrip(address);
5924         if (*address == '\0') {
5925                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
5926         }
5927
5928         g_signal_connect(G_OBJECT(menuitem), "activate",
5929                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
5930         gtk_widget_show(menuitem);
5931 }
5932
5933 static void compose_create_header_entry(Compose *compose) 
5934 {
5935         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5936
5937         GtkWidget *combo;
5938         GtkWidget *entry;
5939         GList *combo_list = NULL;
5940         gchar **string;
5941         const gchar *header = NULL;
5942         ComposeHeaderEntry *headerentry;
5943         gboolean standard_header = FALSE;
5944
5945         headerentry = g_new0(ComposeHeaderEntry, 1);
5946
5947         /* Combo box */
5948         combo = gtk_combo_new();
5949         string = headers; 
5950         while(*string != NULL) {
5951                 combo_list = g_list_append(combo_list, (gchar*)prefs_common_translated_header_name(*string));
5952                 string++;
5953         }
5954         gtk_combo_set_popdown_strings(GTK_COMBO(combo), combo_list);
5955         g_list_free(combo_list);
5956         gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(combo)->entry), TRUE);
5957         g_signal_connect(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
5958                          G_CALLBACK(compose_grab_focus_cb), compose);
5959         gtk_widget_show(combo);
5960         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
5961                         compose->header_nextrow, compose->header_nextrow+1,
5962                         GTK_SHRINK, GTK_FILL, 0, 0);
5963         if (compose->header_last) {     
5964                 const gchar *last_header_entry = gtk_entry_get_text(
5965                                 GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
5966                 string = headers;
5967                 while (*string != NULL) {
5968                         if (!strcmp(*string, last_header_entry))
5969                                 standard_header = TRUE;
5970                         string++;
5971                 }
5972                 if (standard_header)
5973                         header = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
5974         }
5975         if (!compose->header_last || !standard_header) {
5976                 switch(compose->account->protocol) {
5977                         case A_NNTP:
5978                                 header = prefs_common_translated_header_name("Newsgroups:");
5979                                 break;
5980                         default:
5981                                 header = prefs_common_translated_header_name("To:");
5982                                 break;
5983                 }                                                                   
5984         }
5985         if (header)
5986                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), header);
5987
5988         g_signal_connect_after(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
5989                          G_CALLBACK(compose_grab_focus_cb), compose);
5990
5991         /* Entry field */
5992         entry = gtk_entry_new(); 
5993         gtk_widget_show(entry);
5994         gtk_tooltips_set_tip(compose->tooltips, entry,
5995                 _("Use <tab> to autocomplete from addressbook"), NULL);
5996         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
5997                         compose->header_nextrow, compose->header_nextrow+1,
5998                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
5999
6000         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6001                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6002                          headerentry);
6003         g_signal_connect(G_OBJECT(entry), "changed", 
6004                          G_CALLBACK(compose_headerentry_changed_cb), 
6005                          headerentry);
6006         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6007                          G_CALLBACK(compose_grab_focus_cb), compose);
6008                          
6009         /* email dnd */
6010         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6011                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6012                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6013         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6014                          G_CALLBACK(compose_header_drag_received_cb),
6015                          entry);
6016         g_signal_connect(G_OBJECT(entry), "drag-drop",
6017                          G_CALLBACK(compose_drag_drop),
6018                          compose);
6019         g_signal_connect(G_OBJECT(entry), "populate-popup",
6020                          G_CALLBACK(compose_entry_popup_extend),
6021                          NULL);
6022         
6023         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6024
6025         headerentry->compose = compose;
6026         headerentry->combo = combo;
6027         headerentry->entry = entry;
6028         headerentry->headernum = compose->header_nextrow;
6029
6030         compose->header_nextrow++;
6031         compose->header_last = headerentry;             
6032         compose->header_list =
6033                 g_slist_append(compose->header_list,
6034                                headerentry);
6035 }
6036
6037 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6038 {
6039         ComposeHeaderEntry *last_header;
6040         
6041         last_header = compose->header_last;
6042
6043         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(last_header->combo)->entry), header);
6044         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6045 }
6046
6047 static void compose_remove_header_entries(Compose *compose) 
6048 {
6049         GSList *list;
6050         for (list = compose->header_list; list; list = list->next) {
6051                 ComposeHeaderEntry *headerentry = 
6052                         (ComposeHeaderEntry *)list->data;
6053                 gtk_widget_destroy(headerentry->combo);
6054                 gtk_widget_destroy(headerentry->entry);
6055                 g_free(headerentry);
6056         }
6057         compose->header_last = NULL;
6058         g_slist_free(compose->header_list);
6059         compose->header_list = NULL;
6060         compose->header_nextrow = 1;
6061         compose_create_header_entry(compose);
6062 }
6063
6064 static GtkWidget *compose_create_header(Compose *compose) 
6065 {
6066         GtkWidget *from_optmenu_hbox;
6067         GtkWidget *header_scrolledwin;
6068         GtkWidget *header_table;
6069
6070         gint count = 0;
6071
6072         /* header labels and entries */
6073         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6074         gtk_widget_show(header_scrolledwin);
6075         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6076
6077         header_table = gtk_table_new(2, 2, FALSE);
6078         gtk_widget_show(header_table);
6079         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6080         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6081         gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_ETCHED_IN);
6082         count = 0;
6083
6084         /* option menu for selecting accounts */
6085         from_optmenu_hbox = compose_account_option_menu_create(compose);
6086         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6087                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6088         count++;
6089
6090         compose->header_table = header_table;
6091         compose->header_list = NULL;
6092         compose->header_nextrow = count;
6093
6094         compose_create_header_entry(compose);
6095
6096         compose->table            = NULL;
6097
6098         return header_scrolledwin ;
6099 }
6100
6101 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6102 {
6103         Compose *compose = (Compose *)data;
6104         GdkEventButton event;
6105         
6106         event.button = 3;
6107         event.time = gtk_get_current_event_time();
6108
6109         return attach_button_pressed(compose->attach_clist, &event, compose);
6110 }
6111
6112 static GtkWidget *compose_create_attach(Compose *compose)
6113 {
6114         GtkWidget *attach_scrwin;
6115         GtkWidget *attach_clist;
6116
6117         GtkListStore *store;
6118         GtkCellRenderer *renderer;
6119         GtkTreeViewColumn *column;
6120         GtkTreeSelection *selection;
6121
6122         /* attachment list */
6123         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6124         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6125                                        GTK_POLICY_AUTOMATIC,
6126                                        GTK_POLICY_AUTOMATIC);
6127         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6128
6129         store = gtk_list_store_new(N_ATTACH_COLS, 
6130                                    G_TYPE_STRING,
6131                                    G_TYPE_STRING,
6132                                    G_TYPE_STRING,
6133                                    G_TYPE_POINTER,
6134                                    G_TYPE_AUTO_POINTER,
6135                                    -1);
6136         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6137                                         (GTK_TREE_MODEL(store)));
6138         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6139         g_object_unref(store);
6140         
6141         renderer = gtk_cell_renderer_text_new();
6142         column = gtk_tree_view_column_new_with_attributes
6143                         (_("Mime type"), renderer, "text", 
6144                          COL_MIMETYPE, NULL);
6145         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6146         
6147         renderer = gtk_cell_renderer_text_new();
6148         column = gtk_tree_view_column_new_with_attributes
6149                         (_("Size"), renderer, "text", 
6150                          COL_SIZE, NULL);
6151         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6152         
6153         renderer = gtk_cell_renderer_text_new();
6154         column = gtk_tree_view_column_new_with_attributes
6155                         (_("Name"), renderer, "text", 
6156                          COL_NAME, NULL);
6157         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6158
6159         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6160                                      prefs_common.use_stripes_everywhere);
6161         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6162         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6163
6164         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6165                          G_CALLBACK(attach_selected), compose);
6166         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6167                          G_CALLBACK(attach_button_pressed), compose);
6168 #ifndef MAEMO
6169         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6170                          G_CALLBACK(popup_attach_button_pressed), compose);
6171 #else
6172         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6173                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6174         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6175                          G_CALLBACK(popup_attach_button_pressed), compose);
6176 #endif
6177         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6178                          G_CALLBACK(attach_key_pressed), compose);
6179
6180         /* drag and drop */
6181         gtk_drag_dest_set(attach_clist,
6182                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6183                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6184                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6185         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6186                          G_CALLBACK(compose_attach_drag_received_cb),
6187                          compose);
6188         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6189                          G_CALLBACK(compose_drag_drop),
6190                          compose);
6191
6192         compose->attach_scrwin = attach_scrwin;
6193         compose->attach_clist  = attach_clist;
6194
6195         return attach_scrwin;
6196 }
6197
6198 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6199 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6200
6201 static GtkWidget *compose_create_others(Compose *compose)
6202 {
6203         GtkWidget *table;
6204         GtkWidget *savemsg_checkbtn;
6205         GtkWidget *savemsg_entry;
6206         GtkWidget *savemsg_select;
6207         
6208         guint rowcount = 0;
6209         gchar *folderidentifier;
6210
6211         /* Table for settings */
6212         table = gtk_table_new(3, 1, FALSE);
6213         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6214         gtk_widget_show(table);
6215         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6216         rowcount = 0;
6217
6218         /* Save Message to folder */
6219         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6220         gtk_widget_show(savemsg_checkbtn);
6221         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6222         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6223                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6224         }
6225         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6226                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6227
6228         savemsg_entry = gtk_entry_new();
6229         gtk_widget_show(savemsg_entry);
6230         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6231         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6232         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6233                          G_CALLBACK(compose_grab_focus_cb), compose);
6234         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6235                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6236                                   (compose->account, F_OUTBOX));
6237                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6238                 g_free(folderidentifier);
6239         }
6240
6241         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6242         gtk_widget_show(savemsg_select);
6243         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6244         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6245                          G_CALLBACK(compose_savemsg_select_cb),
6246                          compose);
6247
6248         rowcount++;
6249
6250         compose->savemsg_checkbtn = savemsg_checkbtn;
6251         compose->savemsg_entry = savemsg_entry;
6252
6253         return table;   
6254 }
6255
6256 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6257 {
6258         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6259                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6260 }
6261
6262 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6263 {
6264         FolderItem *dest;
6265         gchar * path;
6266
6267         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
6268         if (!dest) return;
6269
6270         path = folder_item_get_identifier(dest);
6271
6272         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6273         g_free(path);
6274 }
6275
6276 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6277                                   GdkAtom clip, GtkTextIter *insert_place);
6278
6279
6280 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6281                                        Compose *compose)
6282 {
6283         gint prev_autowrap;
6284         GtkTextBuffer *buffer;
6285 #if USE_ASPELL
6286         if (event->button == 3) {
6287                 GtkTextIter iter;
6288                 GtkTextIter sel_start, sel_end;
6289                 gboolean stuff_selected;
6290                 gint x, y;
6291                 /* move the cursor to allow GtkAspell to check the word
6292                  * under the mouse */
6293                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6294                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6295                         &x, &y);
6296                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6297                         &iter, x, y);
6298                 /* get selection */
6299                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6300                                 GTK_TEXT_VIEW(text)->buffer,
6301                                 &sel_start, &sel_end);
6302
6303                 gtk_text_buffer_place_cursor (GTK_TEXT_VIEW(text)->buffer, &iter);
6304                 /* reselect stuff */
6305                 if (stuff_selected 
6306                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6307                         gtk_text_buffer_select_range(GTK_TEXT_VIEW(text)->buffer,
6308                                 &sel_start, &sel_end);
6309                 }
6310                 return FALSE; /* pass the event so that the right-click goes through */
6311         }
6312 #endif
6313         if (event->button == 2) {
6314                 GtkTextIter iter;
6315                 gint x, y;
6316                 BLOCK_WRAP();
6317                 
6318                 /* get the middle-click position to paste at the correct place */
6319                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6320                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6321                         &x, &y);
6322                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6323                         &iter, x, y);
6324                 
6325                 entry_paste_clipboard(compose, text, 
6326                                 prefs_common.linewrap_pastes,
6327                                 GDK_SELECTION_PRIMARY, &iter);
6328                 UNBLOCK_WRAP();
6329                 return TRUE;
6330         }
6331         return FALSE;
6332 }
6333
6334 #if USE_ASPELL
6335 static void compose_spell_menu_changed(void *data)
6336 {
6337         Compose *compose = (Compose *)data;
6338         GSList *items;
6339         GtkWidget *menuitem;
6340         GtkWidget *parent_item;
6341         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6342         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
6343         GSList *spell_menu;
6344
6345         if (compose->gtkaspell == NULL)
6346                 return;
6347
6348         parent_item = gtk_item_factory_get_item(ifactory, 
6349                         "/Spelling/Options");
6350
6351         /* setting the submenu removes /Spelling/Options from the factory 
6352          * so we need to save it */
6353
6354         if (parent_item == NULL) {
6355                 parent_item = compose->aspell_options_menu;
6356                 gtk_menu_item_remove_submenu(GTK_MENU_ITEM(parent_item));
6357         } else
6358                 compose->aspell_options_menu = parent_item;
6359
6360         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6361
6362         spell_menu = g_slist_reverse(spell_menu);
6363         for (items = spell_menu;
6364              items; items = items->next) {
6365                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6366                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6367                 gtk_widget_show(GTK_WIDGET(menuitem));
6368         }
6369         g_slist_free(spell_menu);
6370
6371         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6372         
6373 }
6374 #endif
6375
6376 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6377 {
6378         Compose *compose = (Compose *)data;
6379         GdkEventButton event;
6380         
6381         event.button = 3;
6382         event.time = gtk_get_current_event_time();
6383
6384         return text_clicked(compose->text, &event, compose);
6385 }
6386
6387 static gboolean compose_force_window_origin = TRUE;
6388 static Compose *compose_create(PrefsAccount *account,
6389                                                  FolderItem *folder,
6390                                                  ComposeMode mode,
6391                                                  gboolean batch)
6392 {
6393         Compose   *compose;
6394         GtkWidget *window;
6395         GtkWidget *vbox;
6396         GtkWidget *menubar;
6397         GtkWidget *handlebox;
6398
6399         GtkWidget *notebook;
6400
6401         GtkWidget *vbox2;
6402
6403         GtkWidget *label;
6404         GtkWidget *subject_hbox;
6405         GtkWidget *subject_frame;
6406         GtkWidget *subject_entry;
6407         GtkWidget *subject;
6408         GtkWidget *paned;
6409
6410         GtkWidget *edit_vbox;
6411         GtkWidget *ruler_hbox;
6412         GtkWidget *ruler;
6413         GtkWidget *scrolledwin;
6414         GtkWidget *text;
6415         GtkTextBuffer *buffer;
6416         GtkClipboard *clipboard;
6417
6418         UndoMain *undostruct;
6419
6420         gchar *titles[N_ATTACH_COLS];
6421         guint n_menu_entries;
6422         GtkWidget *popupmenu;
6423         GtkItemFactory *popupfactory;
6424         GtkItemFactory *ifactory;
6425         GtkWidget *tmpl_menu;
6426         gint n_entries;
6427         GtkWidget *menuitem;
6428
6429 #if USE_ASPELL
6430         GtkAspell * gtkaspell = NULL;
6431 #endif
6432
6433         static GdkGeometry geometry;
6434
6435         g_return_val_if_fail(account != NULL, NULL);
6436
6437         debug_print("Creating compose window...\n");
6438         compose = g_new0(Compose, 1);
6439
6440         titles[COL_MIMETYPE] = _("MIME type");
6441         titles[COL_SIZE]     = _("Size");
6442         titles[COL_NAME]     = _("Name");
6443
6444         compose->batch = batch;
6445         compose->account = account;
6446         compose->folder = folder;
6447         
6448         compose->mutex = g_mutex_new();
6449         compose->set_cursor_pos = -1;
6450
6451         compose->tooltips = gtk_tooltips_new();
6452
6453         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6454
6455         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6456         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6457
6458         if (!geometry.max_width) {
6459                 geometry.max_width = gdk_screen_width();
6460                 geometry.max_height = gdk_screen_height();
6461         }
6462
6463         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6464                                       &geometry, GDK_HINT_MAX_SIZE);
6465         if (!geometry.min_width) {
6466                 geometry.min_width = 600;
6467                 geometry.min_height = 480;
6468         }
6469         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6470                                       &geometry, GDK_HINT_MIN_SIZE);
6471
6472 #ifndef MAEMO   
6473         if (compose_force_window_origin)
6474                 gtk_widget_set_uposition(window, prefs_common.compose_x, 
6475                                  prefs_common.compose_y);
6476 #endif
6477         g_signal_connect(G_OBJECT(window), "delete_event",
6478                          G_CALLBACK(compose_delete_cb), compose);
6479         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6480         gtk_widget_realize(window);
6481
6482         gtkut_widget_set_composer_icon(window);
6483
6484         vbox = gtk_vbox_new(FALSE, 0);
6485         gtk_container_add(GTK_CONTAINER(window), vbox);
6486
6487         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
6488         menubar = menubar_create(window, compose_entries,
6489                                  n_menu_entries, "<Compose>", compose);
6490         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6491
6492         if (prefs_common.toolbar_detachable) {
6493                 handlebox = gtk_handle_box_new();
6494         } else {
6495                 handlebox = gtk_hbox_new(FALSE, 0);
6496         }
6497         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6498
6499         gtk_widget_realize(handlebox);
6500 #ifdef MAEMO
6501         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6502                                           (gpointer)compose);
6503 #else
6504         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6505                                           (gpointer)compose);
6506 #endif
6507
6508         vbox2 = gtk_vbox_new(FALSE, 2);
6509         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6510         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6511         
6512         /* Notebook */
6513         notebook = gtk_notebook_new();
6514         gtk_widget_set_size_request(notebook, -1, 130);
6515         gtk_widget_show(notebook);
6516
6517         /* header labels and entries */
6518         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6519                         compose_create_header(compose),
6520                         gtk_label_new_with_mnemonic(_("Hea_der")));
6521         /* attachment list */
6522         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6523                         compose_create_attach(compose),
6524                         gtk_label_new_with_mnemonic(_("_Attachments")));
6525         /* Others Tab */
6526         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6527                         compose_create_others(compose),
6528                         gtk_label_new_with_mnemonic(_("Othe_rs")));
6529
6530         /* Subject */
6531         subject_hbox = gtk_hbox_new(FALSE, 0);
6532         gtk_widget_show(subject_hbox);
6533
6534         subject_frame = gtk_frame_new(NULL);
6535         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6536         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6537         gtk_widget_show(subject_frame);
6538
6539         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6540         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6541         gtk_widget_show(subject);
6542
6543         label = gtk_label_new(_("Subject:"));
6544         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6545         gtk_widget_show(label);
6546
6547         subject_entry = gtk_entry_new();
6548         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6549         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6550                          G_CALLBACK(compose_grab_focus_cb), compose);
6551         gtk_widget_show(subject_entry);
6552         compose->subject_entry = subject_entry;
6553         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6554         
6555         edit_vbox = gtk_vbox_new(FALSE, 0);
6556
6557         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6558
6559         /* ruler */
6560         ruler_hbox = gtk_hbox_new(FALSE, 0);
6561         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6562
6563         ruler = gtk_shruler_new();
6564         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6565         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6566                            BORDER_WIDTH);
6567
6568         /* text widget */
6569         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6570         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6571                                        GTK_POLICY_AUTOMATIC,
6572                                        GTK_POLICY_AUTOMATIC);
6573         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6574                                             GTK_SHADOW_IN);
6575         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6576         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6577
6578         text = gtk_text_view_new();
6579         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6580         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6581         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6582         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6583         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6584         
6585         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6586
6587         g_signal_connect_after(G_OBJECT(text), "size_allocate",
6588                                G_CALLBACK(compose_edit_size_alloc),
6589                                ruler);
6590         g_signal_connect(G_OBJECT(buffer), "changed",
6591                          G_CALLBACK(compose_changed_cb), compose);
6592         g_signal_connect(G_OBJECT(text), "grab_focus",
6593                          G_CALLBACK(compose_grab_focus_cb), compose);
6594         g_signal_connect(G_OBJECT(buffer), "insert_text",
6595                          G_CALLBACK(text_inserted), compose);
6596         g_signal_connect(G_OBJECT(text), "button_press_event",
6597                          G_CALLBACK(text_clicked), compose);
6598 #ifndef MAEMO
6599         g_signal_connect(G_OBJECT(text), "popup-menu",
6600                          G_CALLBACK(compose_popup_menu), compose);
6601 #else
6602         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6603                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6604         g_signal_connect(G_OBJECT(text), "tap-and-hold",
6605                          G_CALLBACK(compose_popup_menu), compose);
6606 #endif
6607         g_signal_connect(G_OBJECT(subject_entry), "changed",
6608                          G_CALLBACK(compose_changed_cb), compose);
6609
6610         /* drag and drop */
6611         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6612                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6613                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6614         g_signal_connect(G_OBJECT(text), "drag_data_received",
6615                          G_CALLBACK(compose_insert_drag_received_cb),
6616                          compose);
6617         g_signal_connect(G_OBJECT(text), "drag-drop",
6618                          G_CALLBACK(compose_drag_drop),
6619                          compose);
6620         gtk_widget_show_all(vbox);
6621
6622         /* pane between attach clist and text */
6623         paned = gtk_vpaned_new();
6624         gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
6625         gtk_container_add(GTK_CONTAINER(vbox2), paned);
6626 #ifdef MAEMO
6627         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
6628                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
6629         else
6630                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
6631 #endif
6632         gtk_paned_add1(GTK_PANED(paned), notebook);
6633         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
6634         gtk_widget_show_all(paned);
6635
6636
6637         if (prefs_common.textfont) {
6638                 PangoFontDescription *font_desc;
6639
6640                 font_desc = pango_font_description_from_string
6641                         (prefs_common.textfont);
6642                 if (font_desc) {
6643                         gtk_widget_modify_font(text, font_desc);
6644                         pango_font_description_free(font_desc);
6645                 }
6646         }
6647
6648         n_entries = sizeof(compose_popup_entries) /
6649                 sizeof(compose_popup_entries[0]);
6650         popupmenu = menu_create_items(compose_popup_entries, n_entries,
6651                                       "<Compose>", &popupfactory,
6652                                       compose);
6653
6654         ifactory = gtk_item_factory_from_widget(menubar);
6655         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
6656         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
6657         menu_set_sensitive(ifactory, "/Options/Remove references", FALSE);
6658
6659         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6660
6661         undostruct = undo_init(text);
6662         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
6663                                    menubar);
6664
6665         address_completion_start(window);
6666
6667         compose->window        = window;
6668         compose->vbox          = vbox;
6669         compose->menubar       = menubar;
6670         compose->handlebox     = handlebox;
6671
6672         compose->vbox2         = vbox2;
6673
6674         compose->paned = paned;
6675
6676         compose->notebook      = notebook;
6677         compose->edit_vbox     = edit_vbox;
6678         compose->ruler_hbox    = ruler_hbox;
6679         compose->ruler         = ruler;
6680         compose->scrolledwin   = scrolledwin;
6681         compose->text          = text;
6682
6683         compose->focused_editable = NULL;
6684
6685         compose->popupmenu    = popupmenu;
6686         compose->popupfactory = popupfactory;
6687
6688         compose->tmpl_menu = tmpl_menu;
6689
6690         compose->mode = mode;
6691         compose->rmode = mode;
6692
6693         compose->targetinfo = NULL;
6694         compose->replyinfo  = NULL;
6695         compose->fwdinfo    = NULL;
6696
6697         compose->replyto     = NULL;
6698         compose->cc          = NULL;
6699         compose->bcc         = NULL;
6700         compose->followup_to = NULL;
6701
6702         compose->ml_post     = NULL;
6703
6704         compose->inreplyto   = NULL;
6705         compose->references  = NULL;
6706         compose->msgid       = NULL;
6707         compose->boundary    = NULL;
6708
6709         compose->autowrap       = prefs_common.autowrap;
6710
6711         compose->use_signing    = FALSE;
6712         compose->use_encryption = FALSE;
6713         compose->privacy_system = NULL;
6714
6715         compose->modified = FALSE;
6716
6717         compose->return_receipt = FALSE;
6718
6719         compose->to_list        = NULL;
6720         compose->newsgroup_list = NULL;
6721
6722         compose->undostruct = undostruct;
6723
6724         compose->sig_str = NULL;
6725
6726         compose->exteditor_file    = NULL;
6727         compose->exteditor_pid     = -1;
6728         compose->exteditor_tag     = -1;
6729         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
6730
6731 #if USE_ASPELL
6732         menu_set_sensitive(ifactory, "/Spelling", FALSE);
6733         if (mode != COMPOSE_REDIRECT) {
6734                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
6735                     strcmp(prefs_common.dictionary, "")) {
6736                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
6737                                                   prefs_common.dictionary,
6738                                                   prefs_common.alt_dictionary,
6739                                                   conv_get_locale_charset_str(),
6740                                                   prefs_common.misspelled_col,
6741                                                   prefs_common.check_while_typing,
6742                                                   prefs_common.recheck_when_changing_dict,
6743                                                   prefs_common.use_alternate,
6744                                                   prefs_common.use_both_dicts,
6745                                                   GTK_TEXT_VIEW(text),
6746                                                   GTK_WINDOW(compose->window),
6747                                                   compose_spell_menu_changed,
6748                                                   compose);
6749                         if (!gtkaspell) {
6750                                 alertpanel_error(_("Spell checker could not "
6751                                                 "be started.\n%s"),
6752                                                 gtkaspell_checkers_strerror());
6753                                 gtkaspell_checkers_reset_error();
6754                         } else {
6755                                 if (!gtkaspell_set_sug_mode(gtkaspell,
6756                                                 prefs_common.aspell_sugmode)) {
6757                                         debug_print("Aspell: could not set "
6758                                                     "suggestion mode %s\n",
6759                                                     gtkaspell_checkers_strerror());
6760                                         gtkaspell_checkers_reset_error();
6761                                 }
6762
6763                                 menu_set_sensitive(ifactory, "/Spelling", TRUE);
6764                         }
6765                 }
6766         }
6767         compose->gtkaspell = gtkaspell;
6768         compose_spell_menu_changed(compose);
6769 #endif
6770
6771         compose_select_account(compose, account, TRUE);
6772
6773         menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
6774         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
6775                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
6776
6777         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
6778                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
6779         
6780         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
6781                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
6782
6783         menu_set_sensitive(ifactory, "/Options/Reply mode", compose->mode == COMPOSE_REPLY);
6784
6785         if (account->protocol != A_NNTP)
6786                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry),
6787                                 prefs_common_translated_header_name("To:"));
6788         else
6789                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry),
6790                                 prefs_common_translated_header_name("Newsgroups:"));
6791
6792         addressbook_set_target_compose(compose);
6793         
6794         if (mode != COMPOSE_REDIRECT)
6795                 compose_set_template_menu(compose);
6796         else {
6797                 menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6798                 menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
6799         }
6800
6801         compose_list = g_list_append(compose_list, compose);
6802
6803         if (!prefs_common.show_ruler)
6804                 gtk_widget_hide(ruler_hbox);
6805                 
6806         menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Show ruler");
6807         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
6808                                        prefs_common.show_ruler);
6809
6810         /* Priority */
6811         compose->priority = PRIORITY_NORMAL;
6812         compose_update_priority_menu_item(compose);
6813
6814         compose_set_out_encoding(compose);
6815         
6816         /* Actions menu */
6817         compose_update_actions_menu(compose);
6818
6819         /* Privacy Systems menu */
6820         compose_update_privacy_systems_menu(compose);
6821
6822         activate_privacy_system(compose, account, TRUE);
6823         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
6824         if (batch) {
6825                 gtk_widget_realize(window);
6826         } else {
6827                 gtk_widget_show(window);
6828 #ifdef MAEMO
6829                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
6830                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
6831 #endif
6832         }
6833         
6834         return compose;
6835 }
6836
6837 static GtkWidget *compose_account_option_menu_create(Compose *compose)
6838 {
6839         GList *accounts;
6840         GtkWidget *hbox;
6841         GtkWidget *optmenu;
6842         GtkWidget *optmenubox;
6843         GtkListStore *menu;
6844         GtkTreeIter iter;
6845         GtkWidget *from_name = NULL;
6846
6847         gint num = 0, def_menu = 0;
6848         
6849         accounts = account_get_list();
6850         g_return_val_if_fail(accounts != NULL, NULL);
6851
6852         optmenubox = gtk_event_box_new();
6853         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
6854         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
6855
6856         hbox = gtk_hbox_new(FALSE, 6);
6857         from_name = gtk_entry_new();
6858         
6859         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
6860                          G_CALLBACK(compose_grab_focus_cb), compose);
6861
6862         for (; accounts != NULL; accounts = accounts->next, num++) {
6863                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
6864                 gchar *name, *from = NULL;
6865
6866                 if (ac == compose->account) def_menu = num;
6867
6868                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
6869                                        ac->account_name);
6870                 
6871                 if (ac == compose->account) {
6872                         if (ac->name && *ac->name) {
6873                                 gchar *buf;
6874                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
6875                                 from = g_strdup_printf("%s <%s>",
6876                                                        buf, ac->address);
6877                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
6878                         } else {
6879                                 from = g_strdup_printf("%s",
6880                                                        ac->address);
6881                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
6882                         }
6883                 }
6884                 COMBOBOX_ADD(menu, name, ac->account_id);
6885                 g_free(name);
6886                 g_free(from);
6887         }
6888
6889         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
6890
6891         g_signal_connect(G_OBJECT(optmenu), "changed",
6892                         G_CALLBACK(account_activated),
6893                         compose);
6894         g_signal_connect(G_OBJECT(from_name), "populate-popup",
6895                          G_CALLBACK(compose_entry_popup_extend),
6896                          NULL);
6897
6898         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
6899         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
6900         
6901         gtk_tooltips_set_tip(compose->tooltips, optmenubox,
6902                 _("Account to use for this email"), NULL);
6903         gtk_tooltips_set_tip(compose->tooltips, from_name,
6904                 _("Sender address to be used"), NULL);
6905
6906         compose->from_name = from_name;
6907         
6908         return hbox;
6909 }
6910
6911 static void compose_set_priority_cb(gpointer data,
6912                                     guint action,
6913                                     GtkWidget *widget)
6914 {
6915         Compose *compose = (Compose *) data;
6916         compose->priority = action;
6917 }
6918
6919 static void compose_reply_change_mode(gpointer data,
6920                                     ComposeMode action,
6921                                     GtkWidget *widget)
6922 {
6923         Compose *compose = (Compose *) data;
6924         gboolean was_modified = compose->modified;
6925
6926         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
6927         
6928         g_return_if_fail(compose->replyinfo != NULL);
6929         
6930         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
6931                 ml = TRUE;
6932         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
6933                 followup = TRUE;
6934         if (action == COMPOSE_REPLY_TO_ALL)
6935                 all = TRUE;
6936         if (action == COMPOSE_REPLY_TO_SENDER)
6937                 sender = TRUE;
6938         if (action == COMPOSE_REPLY_TO_LIST)
6939                 ml = TRUE;
6940
6941         compose_remove_header_entries(compose);
6942         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
6943         if (compose->account->set_autocc && compose->account->auto_cc)
6944                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
6945
6946         if (compose->account->set_autobcc && compose->account->auto_bcc) 
6947                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
6948         
6949         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
6950                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
6951         compose_show_first_last_header(compose, TRUE);
6952         compose->modified = was_modified;
6953         compose_set_title(compose);
6954 }
6955
6956 static void compose_update_priority_menu_item(Compose * compose)
6957 {
6958         GtkItemFactory *ifactory;
6959         GtkWidget *menuitem = NULL;
6960
6961         ifactory = gtk_item_factory_from_widget(compose->menubar);
6962         
6963         switch (compose->priority) {
6964                 case PRIORITY_HIGHEST:
6965                         menuitem = gtk_item_factory_get_item
6966                                 (ifactory, "/Options/Priority/Highest");
6967                         break;
6968                 case PRIORITY_HIGH:
6969                         menuitem = gtk_item_factory_get_item
6970                                 (ifactory, "/Options/Priority/High");
6971                         break;
6972                 case PRIORITY_NORMAL:
6973                         menuitem = gtk_item_factory_get_item
6974                                 (ifactory, "/Options/Priority/Normal");
6975                         break;
6976                 case PRIORITY_LOW:
6977                         menuitem = gtk_item_factory_get_item
6978                                 (ifactory, "/Options/Priority/Low");
6979                         break;
6980                 case PRIORITY_LOWEST:
6981                         menuitem = gtk_item_factory_get_item
6982                                 (ifactory, "/Options/Priority/Lowest");
6983                         break;
6984         }
6985         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
6986 }       
6987
6988 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
6989 {
6990         Compose *compose = (Compose *) data;
6991         gchar *systemid;
6992         GtkItemFactory *ifactory;
6993         gboolean can_sign = FALSE, can_encrypt = FALSE;
6994
6995         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
6996
6997         if (!GTK_CHECK_MENU_ITEM(widget)->active)
6998                 return;
6999
7000         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7001         g_free(compose->privacy_system);
7002         compose->privacy_system = NULL;
7003         if (systemid != NULL) {
7004                 compose->privacy_system = g_strdup(systemid);
7005
7006                 can_sign = privacy_system_can_sign(systemid);
7007                 can_encrypt = privacy_system_can_encrypt(systemid);
7008         }
7009
7010         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7011
7012         ifactory = gtk_item_factory_from_widget(compose->menubar);
7013         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7014         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7015 }
7016
7017 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7018 {
7019         static gchar *branch_path = "/Options/Privacy System";
7020         GtkItemFactory *ifactory;
7021         GtkWidget *menuitem = NULL;
7022         GList *amenu;
7023         gboolean can_sign = FALSE, can_encrypt = FALSE;
7024         gboolean found = FALSE;
7025
7026         ifactory = gtk_item_factory_from_widget(compose->menubar);
7027
7028         if (compose->privacy_system != NULL) {
7029                 gchar *systemid;
7030
7031                 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7032                 g_return_if_fail(menuitem != NULL);
7033
7034                 amenu = GTK_MENU_SHELL(menuitem)->children;
7035                 menuitem = NULL;
7036                 while (amenu != NULL) {
7037                         GList *alist = amenu->next;
7038
7039                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7040                         if (systemid != NULL) {
7041                                 if (strcmp(systemid, compose->privacy_system) == 0) {
7042                                         menuitem = GTK_WIDGET(amenu->data);
7043
7044                                         can_sign = privacy_system_can_sign(systemid);
7045                                         can_encrypt = privacy_system_can_encrypt(systemid);
7046                                         found = TRUE;
7047                                         break;
7048                                 } 
7049                         } else if (strlen(compose->privacy_system) == 0) {
7050                                         menuitem = GTK_WIDGET(amenu->data);
7051
7052                                         can_sign = FALSE;
7053                                         can_encrypt = FALSE;
7054                                         found = TRUE;
7055                                         break;
7056                         }
7057
7058                         amenu = alist;
7059                 }
7060                 if (menuitem != NULL)
7061                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7062                 
7063                 if (warn && !found && strlen(compose->privacy_system)) {
7064                         gchar *tmp = g_strdup_printf(
7065                                 _("The privacy system '%s' cannot be loaded. You "
7066                                   "will not be able to sign or encrypt this message."),
7067                                   compose->privacy_system);
7068                         alertpanel_warning(tmp);
7069                         g_free(tmp);
7070                 }
7071         } 
7072
7073         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7074         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7075 }       
7076  
7077 static void compose_set_out_encoding(Compose *compose)
7078 {
7079         GtkItemFactoryEntry *entry;
7080         GtkItemFactory *ifactory;
7081         CharSet out_encoding;
7082         gchar *path, *p, *q;
7083         GtkWidget *item;
7084
7085         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7086         ifactory = gtk_item_factory_from_widget(compose->menubar);
7087
7088         for (entry = compose_entries; entry->callback != compose_address_cb;
7089              entry++) {
7090                 if (entry->callback == compose_set_encoding_cb &&
7091                     (CharSet)entry->callback_action == out_encoding) {
7092                         p = q = path = g_strdup(entry->path);
7093                         while (*p) {
7094                                 if (*p == '_') {
7095                                         if (p[1] == '_') {
7096                                                 p++;
7097                                                 *q++ = '_';
7098                                         }
7099                                 } else
7100                                         *q++ = *p;
7101                                 p++;
7102                         }
7103                         *q = '\0';
7104                         item = gtk_item_factory_get_item(ifactory, path);
7105                         gtk_widget_activate(item);
7106                         g_free(path);
7107                         break;
7108                 }
7109         }
7110 }
7111
7112 static void compose_set_template_menu(Compose *compose)
7113 {
7114         GSList *tmpl_list, *cur;
7115         GtkWidget *menu;
7116         GtkWidget *item;
7117
7118         tmpl_list = template_get_config();
7119
7120         menu = gtk_menu_new();
7121
7122         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7123                 Template *tmpl = (Template *)cur->data;
7124
7125                 item = gtk_menu_item_new_with_label(tmpl->name);
7126                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7127                 g_signal_connect(G_OBJECT(item), "activate",
7128                                  G_CALLBACK(compose_template_activate_cb),
7129                                  compose);
7130                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7131                 gtk_widget_show(item);
7132         }
7133
7134         gtk_widget_show(menu);
7135         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7136 }
7137
7138 void compose_update_actions_menu(Compose *compose)
7139 {
7140         GtkItemFactory *ifactory;
7141
7142         ifactory = gtk_item_factory_from_widget(compose->menubar);
7143         action_update_compose_menu(ifactory, "/Tools/Actions", compose);
7144 }
7145
7146 static void compose_update_privacy_systems_menu(Compose *compose)
7147 {
7148         static gchar *branch_path = "/Options/Privacy System";
7149         GtkItemFactory *ifactory;
7150         GtkWidget *menuitem;
7151         GSList *systems, *cur;
7152         GList *amenu;
7153         GtkWidget *widget;
7154         GtkWidget *system_none;
7155         GSList *group;
7156
7157         ifactory = gtk_item_factory_from_widget(compose->menubar);
7158
7159         /* remove old entries */
7160         menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7161         g_return_if_fail(menuitem != NULL);
7162
7163         amenu = GTK_MENU_SHELL(menuitem)->children->next;
7164         while (amenu != NULL) {
7165                 GList *alist = amenu->next;
7166                 gtk_widget_destroy(GTK_WIDGET(amenu->data));
7167                 amenu = alist;
7168         }
7169
7170         system_none = gtk_item_factory_get_widget(ifactory,
7171                 "/Options/Privacy System/None");
7172
7173         g_signal_connect(G_OBJECT(system_none), "activate",
7174                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7175
7176         systems = privacy_get_system_ids();
7177         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7178                 gchar *systemid = cur->data;
7179
7180                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7181                 widget = gtk_radio_menu_item_new_with_label(group,
7182                         privacy_system_get_name(systemid));
7183                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7184                                        g_strdup(systemid), g_free);
7185                 g_signal_connect(G_OBJECT(widget), "activate",
7186                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7187
7188                 gtk_menu_append(GTK_MENU(system_none->parent), widget);
7189                 gtk_widget_show(widget);
7190                 g_free(systemid);
7191         }
7192         g_slist_free(systems);
7193 }
7194
7195 void compose_reflect_prefs_all(void)
7196 {
7197         GList *cur;
7198         Compose *compose;
7199
7200         for (cur = compose_list; cur != NULL; cur = cur->next) {
7201                 compose = (Compose *)cur->data;
7202                 compose_set_template_menu(compose);
7203         }
7204 }
7205
7206 void compose_reflect_prefs_pixmap_theme(void)
7207 {
7208         GList *cur;
7209         Compose *compose;
7210
7211         for (cur = compose_list; cur != NULL; cur = cur->next) {
7212                 compose = (Compose *)cur->data;
7213                 toolbar_update(TOOLBAR_COMPOSE, compose);
7214         }
7215 }
7216
7217 static const gchar *compose_quote_char_from_context(Compose *compose)
7218 {
7219         const gchar *qmark = NULL;
7220
7221         g_return_val_if_fail(compose != NULL, NULL);
7222
7223         switch (compose->mode) {
7224                 /* use forward-specific quote char */
7225                 case COMPOSE_FORWARD:
7226                 case COMPOSE_FORWARD_AS_ATTACH:
7227                 case COMPOSE_FORWARD_INLINE:
7228                         if (compose->folder && compose->folder->prefs &&
7229                                         compose->folder->prefs->forward_with_format)
7230                                 qmark = compose->folder->prefs->forward_quotemark;
7231                         else if (compose->account->forward_with_format)
7232                                 qmark = compose->account->forward_quotemark;
7233                         else
7234                                 qmark = prefs_common.fw_quotemark;
7235                         break;
7236
7237                 /* use reply-specific quote char in all other modes */
7238                 default:
7239                         if (compose->folder && compose->folder->prefs &&
7240                                         compose->folder->prefs->reply_with_format)
7241                                 qmark = compose->folder->prefs->reply_quotemark;
7242                         else if (compose->account->reply_with_format)
7243                                 qmark = compose->account->reply_quotemark;
7244                         else
7245                                 qmark = prefs_common.quotemark;
7246                         break;
7247         }
7248
7249         if (qmark == NULL || *qmark == '\0')
7250                 qmark = "> ";
7251
7252         return qmark;
7253 }
7254
7255 static void compose_template_apply(Compose *compose, Template *tmpl,
7256                                    gboolean replace)
7257 {
7258         GtkTextView *text;
7259         GtkTextBuffer *buffer;
7260         GtkTextMark *mark;
7261         GtkTextIter iter;
7262         const gchar *qmark;
7263         gchar *parsed_str = NULL;
7264         gint cursor_pos = 0;
7265         const gchar *err_msg = _("Template body format error at line %d.");
7266         if (!tmpl) return;
7267
7268         /* process the body */
7269
7270         text = GTK_TEXT_VIEW(compose->text);
7271         buffer = gtk_text_view_get_buffer(text);
7272
7273         if (tmpl->value) {
7274                 qmark = compose_quote_char_from_context(compose);
7275
7276                 if (compose->replyinfo != NULL) {
7277
7278                         if (replace)
7279                                 gtk_text_buffer_set_text(buffer, "", -1);
7280                         mark = gtk_text_buffer_get_insert(buffer);
7281                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7282
7283                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7284                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7285
7286                 } else if (compose->fwdinfo != NULL) {
7287
7288                         if (replace)
7289                                 gtk_text_buffer_set_text(buffer, "", -1);
7290                         mark = gtk_text_buffer_get_insert(buffer);
7291                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7292
7293                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7294                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7295
7296                 } else {
7297                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7298
7299                         GtkTextIter start, end;
7300                         gchar *tmp = NULL;
7301
7302                         gtk_text_buffer_get_start_iter(buffer, &start);
7303                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7304                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7305
7306                         /* clear the buffer now */
7307                         if (replace)
7308                                 gtk_text_buffer_set_text(buffer, "", -1);
7309
7310                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7311                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7312                         procmsg_msginfo_free( dummyinfo );
7313
7314                         g_free( tmp );
7315                 } 
7316         } else {
7317                 if (replace)
7318                         gtk_text_buffer_set_text(buffer, "", -1);
7319                 mark = gtk_text_buffer_get_insert(buffer);
7320                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7321         }       
7322
7323         if (replace && parsed_str && compose->account->auto_sig)
7324                 compose_insert_sig(compose, FALSE);
7325
7326         if (replace && parsed_str) {
7327                 gtk_text_buffer_get_start_iter(buffer, &iter);
7328                 gtk_text_buffer_place_cursor(buffer, &iter);
7329         }
7330         
7331         if (parsed_str) {
7332                 cursor_pos = quote_fmt_get_cursor_pos();
7333                 compose->set_cursor_pos = cursor_pos;
7334                 if (cursor_pos == -1)
7335                         cursor_pos = 0;
7336                 gtk_text_buffer_get_start_iter(buffer, &iter);
7337                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7338                 gtk_text_buffer_place_cursor(buffer, &iter);
7339         }
7340
7341         /* process the other fields */
7342
7343         compose_template_apply_fields(compose, tmpl);
7344         quote_fmt_reset_vartable();
7345         compose_changed_cb(NULL, compose);
7346 }
7347
7348 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7349 {
7350         MsgInfo* dummyinfo = NULL;
7351         MsgInfo *msginfo = NULL;
7352         gchar *buf = NULL;
7353
7354         if (compose->replyinfo != NULL)
7355                 msginfo = compose->replyinfo;
7356         else if (compose->fwdinfo != NULL)
7357                 msginfo = compose->fwdinfo;
7358         else {
7359                 dummyinfo = compose_msginfo_new_from_compose(compose);
7360                 msginfo = dummyinfo;
7361         }
7362
7363         if (tmpl->to && *tmpl->to != '\0') {
7364 #ifdef USE_ASPELL
7365                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7366                                 compose->gtkaspell);
7367 #else
7368                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7369 #endif
7370                 quote_fmt_scan_string(tmpl->to);
7371                 quote_fmt_parse();
7372
7373                 buf = quote_fmt_get_buffer();
7374                 if (buf == NULL) {
7375                         alertpanel_error(_("Template To format error."));
7376                 } else {
7377                         compose_entry_append(compose, buf, COMPOSE_TO);
7378                 }
7379         }
7380
7381         if (tmpl->cc && *tmpl->cc != '\0') {
7382 #ifdef USE_ASPELL
7383                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7384                                 compose->gtkaspell);
7385 #else
7386                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7387 #endif
7388                 quote_fmt_scan_string(tmpl->cc);
7389                 quote_fmt_parse();
7390
7391                 buf = quote_fmt_get_buffer();
7392                 if (buf == NULL) {
7393                         alertpanel_error(_("Template Cc format error."));
7394                 } else {
7395                         compose_entry_append(compose, buf, COMPOSE_CC);
7396                 }
7397         }
7398
7399         if (tmpl->bcc && *tmpl->bcc != '\0') {
7400 #ifdef USE_ASPELL
7401                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7402                                 compose->gtkaspell);
7403 #else
7404                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7405 #endif
7406                 quote_fmt_scan_string(tmpl->bcc);
7407                 quote_fmt_parse();
7408
7409                 buf = quote_fmt_get_buffer();
7410                 if (buf == NULL) {
7411                         alertpanel_error(_("Template Bcc format error."));
7412                 } else {
7413                         compose_entry_append(compose, buf, COMPOSE_BCC);
7414                 }
7415         }
7416
7417         /* process the subject */
7418         if (tmpl->subject && *tmpl->subject != '\0') {
7419 #ifdef USE_ASPELL
7420                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7421                                 compose->gtkaspell);
7422 #else
7423                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7424 #endif
7425                 quote_fmt_scan_string(tmpl->subject);
7426                 quote_fmt_parse();
7427
7428                 buf = quote_fmt_get_buffer();
7429                 if (buf == NULL) {
7430                         alertpanel_error(_("Template subject format error."));
7431                 } else {
7432                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7433                 }
7434         }
7435
7436         procmsg_msginfo_free( dummyinfo );
7437 }
7438
7439 static void compose_destroy(Compose *compose)
7440 {
7441         GtkTextBuffer *buffer;
7442         GtkClipboard *clipboard;
7443
7444         compose_list = g_list_remove(compose_list, compose);
7445
7446         if (compose->updating) {
7447                 debug_print("danger, not destroying anything now\n");
7448                 compose->deferred_destroy = TRUE;
7449                 return;
7450         }
7451         /* NOTE: address_completion_end() does nothing with the window
7452          * however this may change. */
7453         address_completion_end(compose->window);
7454
7455         slist_free_strings(compose->to_list);
7456         g_slist_free(compose->to_list);
7457         slist_free_strings(compose->newsgroup_list);
7458         g_slist_free(compose->newsgroup_list);
7459         slist_free_strings(compose->header_list);
7460         g_slist_free(compose->header_list);
7461
7462         procmsg_msginfo_free(compose->targetinfo);
7463         procmsg_msginfo_free(compose->replyinfo);
7464         procmsg_msginfo_free(compose->fwdinfo);
7465
7466         g_free(compose->replyto);
7467         g_free(compose->cc);
7468         g_free(compose->bcc);
7469         g_free(compose->newsgroups);
7470         g_free(compose->followup_to);
7471
7472         g_free(compose->ml_post);
7473
7474         g_free(compose->inreplyto);
7475         g_free(compose->references);
7476         g_free(compose->msgid);
7477         g_free(compose->boundary);
7478
7479         g_free(compose->redirect_filename);
7480         if (compose->undostruct)
7481                 undo_destroy(compose->undostruct);
7482
7483         g_free(compose->sig_str);
7484
7485         g_free(compose->exteditor_file);
7486
7487         g_free(compose->orig_charset);
7488
7489         g_free(compose->privacy_system);
7490
7491         if (addressbook_get_target_compose() == compose)
7492                 addressbook_set_target_compose(NULL);
7493
7494 #if USE_ASPELL
7495         if (compose->gtkaspell) {
7496                 gtkaspell_delete(compose->gtkaspell);
7497                 compose->gtkaspell = NULL;
7498         }
7499 #endif
7500
7501         prefs_common.compose_width = compose->scrolledwin->allocation.width;
7502         prefs_common.compose_height = compose->window->allocation.height;
7503
7504         if (!gtk_widget_get_parent(compose->paned))
7505                 gtk_widget_destroy(compose->paned);
7506         gtk_widget_destroy(compose->popupmenu);
7507
7508         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7509         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7510         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7511
7512         gtk_widget_destroy(compose->window);
7513         toolbar_destroy(compose->toolbar);
7514         g_free(compose->toolbar);
7515         g_mutex_free(compose->mutex);
7516         g_free(compose);
7517 }
7518
7519 static void compose_attach_info_free(AttachInfo *ainfo)
7520 {
7521         g_free(ainfo->file);
7522         g_free(ainfo->content_type);
7523         g_free(ainfo->name);
7524         g_free(ainfo);
7525 }
7526
7527 static void compose_attach_remove_selected(Compose *compose)
7528 {
7529         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7530         GtkTreeSelection *selection;
7531         GList *sel, *cur;
7532         GtkTreeModel *model;
7533
7534         selection = gtk_tree_view_get_selection(tree_view);
7535         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7536
7537         if (!sel) 
7538                 return;
7539
7540         for (cur = sel; cur != NULL; cur = cur->next) {
7541                 GtkTreePath *path = cur->data;
7542                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7543                                                 (model, cur->data);
7544                 cur->data = ref;
7545                 gtk_tree_path_free(path);
7546         }
7547
7548         for (cur = sel; cur != NULL; cur = cur->next) {
7549                 GtkTreeRowReference *ref = cur->data;
7550                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7551                 GtkTreeIter iter;
7552
7553                 if (gtk_tree_model_get_iter(model, &iter, path))
7554                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
7555                 
7556                 gtk_tree_path_free(path);
7557                 gtk_tree_row_reference_free(ref);
7558         }
7559
7560         g_list_free(sel);
7561 }
7562
7563 static struct _AttachProperty
7564 {
7565         GtkWidget *window;
7566         GtkWidget *mimetype_entry;
7567         GtkWidget *encoding_optmenu;
7568         GtkWidget *path_entry;
7569         GtkWidget *filename_entry;
7570         GtkWidget *ok_btn;
7571         GtkWidget *cancel_btn;
7572 } attach_prop;
7573
7574 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
7575 {       
7576         gtk_tree_path_free((GtkTreePath *)ptr);
7577 }
7578
7579 static void compose_attach_property(Compose *compose)
7580 {
7581         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7582         AttachInfo *ainfo;
7583         GtkComboBox *optmenu;
7584         GtkTreeSelection *selection;
7585         GList *sel;
7586         GtkTreeModel *model;
7587         GtkTreeIter iter;
7588         GtkTreePath *path;
7589         static gboolean cancelled;
7590
7591         /* only if one selected */
7592         selection = gtk_tree_view_get_selection(tree_view);
7593         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
7594                 return;
7595
7596         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7597         if (!sel)
7598                 return;
7599
7600         path = (GtkTreePath *) sel->data;
7601         gtk_tree_model_get_iter(model, &iter, path);
7602         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
7603         
7604         if (!ainfo) {
7605                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
7606                 g_list_free(sel);
7607                 return;
7608         }               
7609         g_list_free(sel);
7610
7611         if (!attach_prop.window)
7612                 compose_attach_property_create(&cancelled);
7613         gtk_widget_grab_focus(attach_prop.ok_btn);
7614         gtk_widget_show(attach_prop.window);
7615         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
7616
7617         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
7618         if (ainfo->encoding == ENC_UNKNOWN)
7619                 combobox_select_by_data(optmenu, ENC_BASE64);
7620         else
7621                 combobox_select_by_data(optmenu, ainfo->encoding);
7622
7623         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
7624                            ainfo->content_type ? ainfo->content_type : "");
7625         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
7626                            ainfo->file ? ainfo->file : "");
7627         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
7628                            ainfo->name ? ainfo->name : "");
7629
7630         for (;;) {
7631                 const gchar *entry_text;
7632                 gchar *text;
7633                 gchar *cnttype = NULL;
7634                 gchar *file = NULL;
7635                 off_t size = 0;
7636
7637                 cancelled = FALSE;
7638                 gtk_main();
7639
7640                 gtk_widget_hide(attach_prop.window);
7641                 
7642                 if (cancelled) 
7643                         break;
7644
7645                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
7646                 if (*entry_text != '\0') {
7647                         gchar *p;
7648
7649                         text = g_strstrip(g_strdup(entry_text));
7650                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
7651                                 cnttype = g_strdup(text);
7652                                 g_free(text);
7653                         } else {
7654                                 alertpanel_error(_("Invalid MIME type."));
7655                                 g_free(text);
7656                                 continue;
7657                         }
7658                 }
7659
7660                 ainfo->encoding = combobox_get_active_data(optmenu);
7661
7662                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
7663                 if (*entry_text != '\0') {
7664                         if (is_file_exist(entry_text) &&
7665                             (size = get_file_size(entry_text)) > 0)
7666                                 file = g_strdup(entry_text);
7667                         else {
7668                                 alertpanel_error
7669                                         (_("File doesn't exist or is empty."));
7670                                 g_free(cnttype);
7671                                 continue;
7672                         }
7673                 }
7674
7675                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
7676                 if (*entry_text != '\0') {
7677                         g_free(ainfo->name);
7678                         ainfo->name = g_strdup(entry_text);
7679                 }
7680
7681                 if (cnttype) {
7682                         g_free(ainfo->content_type);
7683                         ainfo->content_type = cnttype;
7684                 }
7685                 if (file) {
7686                         g_free(ainfo->file);
7687                         ainfo->file = file;
7688                 }
7689                 if (size)
7690                         ainfo->size = size;
7691
7692                 /* update tree store */
7693                 text = to_human_readable(ainfo->size);
7694                 gtk_tree_model_get_iter(model, &iter, path);
7695                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
7696                                    COL_MIMETYPE, ainfo->content_type,
7697                                    COL_SIZE, text,
7698                                    COL_NAME, ainfo->name,
7699                                    -1);
7700                 
7701                 break;
7702         }
7703
7704         gtk_tree_path_free(path);
7705 }
7706
7707 #define SET_LABEL_AND_ENTRY(str, entry, top) \
7708 { \
7709         label = gtk_label_new(str); \
7710         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
7711                          GTK_FILL, 0, 0, 0); \
7712         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
7713  \
7714         entry = gtk_entry_new(); \
7715         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
7716                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
7717 }
7718
7719 static void compose_attach_property_create(gboolean *cancelled)
7720 {
7721         GtkWidget *window;
7722         GtkWidget *vbox;
7723         GtkWidget *table;
7724         GtkWidget *label;
7725         GtkWidget *mimetype_entry;
7726         GtkWidget *hbox;
7727         GtkWidget *optmenu;
7728         GtkListStore *optmenu_menu;
7729         GtkWidget *path_entry;
7730         GtkWidget *filename_entry;
7731         GtkWidget *hbbox;
7732         GtkWidget *ok_btn;
7733         GtkWidget *cancel_btn;
7734         GList     *mime_type_list, *strlist;
7735         GtkTreeIter iter;
7736
7737         debug_print("Creating attach_property window...\n");
7738
7739         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
7740         gtk_widget_set_size_request(window, 480, -1);
7741         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
7742         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
7743         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
7744         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
7745         g_signal_connect(G_OBJECT(window), "delete_event",
7746                          G_CALLBACK(attach_property_delete_event),
7747                          cancelled);
7748         g_signal_connect(G_OBJECT(window), "key_press_event",
7749                          G_CALLBACK(attach_property_key_pressed),
7750                          cancelled);
7751
7752         vbox = gtk_vbox_new(FALSE, 8);
7753         gtk_container_add(GTK_CONTAINER(window), vbox);
7754
7755         table = gtk_table_new(4, 2, FALSE);
7756         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
7757         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
7758         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
7759
7760         label = gtk_label_new(_("MIME type")); 
7761         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
7762                          GTK_FILL, 0, 0, 0); 
7763         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
7764         mimetype_entry = gtk_combo_new(); 
7765         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
7766                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7767                          
7768         /* stuff with list */
7769         mime_type_list = procmime_get_mime_type_list();
7770         strlist = NULL;
7771         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
7772                 MimeType *type = (MimeType *) mime_type_list->data;
7773                 gchar *tmp;
7774
7775                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
7776
7777                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
7778                         g_free(tmp);
7779                 else
7780                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
7781                                         (GCompareFunc)strcmp2);
7782         }
7783
7784         gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry), strlist);
7785
7786         for (mime_type_list = strlist; mime_type_list != NULL; 
7787                 mime_type_list = mime_type_list->next)
7788                 g_free(mime_type_list->data);
7789         g_list_free(strlist);
7790                          
7791         mimetype_entry = GTK_COMBO(mimetype_entry)->entry;                       
7792
7793         label = gtk_label_new(_("Encoding"));
7794         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
7795                          GTK_FILL, 0, 0, 0);
7796         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
7797
7798         hbox = gtk_hbox_new(FALSE, 0);
7799         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
7800                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7801
7802         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
7803         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7804
7805         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
7806         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
7807         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
7808         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
7809         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
7810
7811         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
7812
7813         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
7814         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
7815
7816         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
7817                                       &ok_btn, GTK_STOCK_OK,
7818                                       NULL, NULL);
7819         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
7820         gtk_widget_grab_default(ok_btn);
7821
7822         g_signal_connect(G_OBJECT(ok_btn), "clicked",
7823                          G_CALLBACK(attach_property_ok),
7824                          cancelled);
7825         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
7826                          G_CALLBACK(attach_property_cancel),
7827                          cancelled);
7828
7829         gtk_widget_show_all(vbox);
7830
7831         attach_prop.window           = window;
7832         attach_prop.mimetype_entry   = mimetype_entry;
7833         attach_prop.encoding_optmenu = optmenu;
7834         attach_prop.path_entry       = path_entry;
7835         attach_prop.filename_entry   = filename_entry;
7836         attach_prop.ok_btn           = ok_btn;
7837         attach_prop.cancel_btn       = cancel_btn;
7838 }
7839
7840 #undef SET_LABEL_AND_ENTRY
7841
7842 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
7843 {
7844         *cancelled = FALSE;
7845         gtk_main_quit();
7846 }
7847
7848 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
7849 {
7850         *cancelled = TRUE;
7851         gtk_main_quit();
7852 }
7853
7854 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
7855                                          gboolean *cancelled)
7856 {
7857         *cancelled = TRUE;
7858         gtk_main_quit();
7859
7860         return TRUE;
7861 }
7862
7863 static gboolean attach_property_key_pressed(GtkWidget *widget,
7864                                             GdkEventKey *event,
7865                                             gboolean *cancelled)
7866 {
7867         if (event && event->keyval == GDK_Escape) {
7868                 *cancelled = TRUE;
7869                 gtk_main_quit();
7870         }
7871         return FALSE;
7872 }
7873
7874 static void compose_exec_ext_editor(Compose *compose)
7875 {
7876 #ifdef G_OS_UNIX
7877         gchar *tmp;
7878         pid_t pid;
7879         gint pipe_fds[2];
7880
7881         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
7882                               G_DIR_SEPARATOR, compose);
7883
7884         if (pipe(pipe_fds) < 0) {
7885                 perror("pipe");
7886                 g_free(tmp);
7887                 return;
7888         }
7889
7890         if ((pid = fork()) < 0) {
7891                 perror("fork");
7892                 g_free(tmp);
7893                 return;
7894         }
7895
7896         if (pid != 0) {
7897                 /* close the write side of the pipe */
7898                 close(pipe_fds[1]);
7899
7900                 compose->exteditor_file    = g_strdup(tmp);
7901                 compose->exteditor_pid     = pid;
7902
7903                 compose_set_ext_editor_sensitive(compose, FALSE);
7904
7905                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
7906                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
7907                                                         G_IO_IN,
7908                                                         compose_input_cb,
7909                                                         compose);
7910         } else {        /* process-monitoring process */
7911                 pid_t pid_ed;
7912
7913                 if (setpgid(0, 0))
7914                         perror("setpgid");
7915
7916                 /* close the read side of the pipe */
7917                 close(pipe_fds[0]);
7918
7919                 if (compose_write_body_to_file(compose, tmp) < 0) {
7920                         fd_write_all(pipe_fds[1], "2\n", 2);
7921                         _exit(1);
7922                 }
7923
7924                 pid_ed = compose_exec_ext_editor_real(tmp);
7925                 if (pid_ed < 0) {
7926                         fd_write_all(pipe_fds[1], "1\n", 2);
7927                         _exit(1);
7928                 }
7929
7930                 /* wait until editor is terminated */
7931                 waitpid(pid_ed, NULL, 0);
7932
7933                 fd_write_all(pipe_fds[1], "0\n", 2);
7934
7935                 close(pipe_fds[1]);
7936                 _exit(0);
7937         }
7938
7939         g_free(tmp);
7940 #endif /* G_OS_UNIX */
7941 }
7942
7943 #ifdef G_OS_UNIX
7944 static gint compose_exec_ext_editor_real(const gchar *file)
7945 {
7946         gchar buf[1024];
7947         gchar *p;
7948         gchar **cmdline;
7949         pid_t pid;
7950
7951         g_return_val_if_fail(file != NULL, -1);
7952
7953         if ((pid = fork()) < 0) {
7954                 perror("fork");
7955                 return -1;
7956         }
7957
7958         if (pid != 0) return pid;
7959
7960         /* grandchild process */
7961
7962         if (setpgid(0, getppid()))
7963                 perror("setpgid");
7964
7965         if (prefs_common.ext_editor_cmd &&
7966             (p = strchr(prefs_common.ext_editor_cmd, '%')) &&
7967             *(p + 1) == 's' && !strchr(p + 2, '%')) {
7968                 g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, file);
7969         } else {
7970                 if (prefs_common.ext_editor_cmd)
7971                         g_warning("External editor command line is invalid: '%s'\n",
7972                                   prefs_common.ext_editor_cmd);
7973                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
7974         }
7975
7976         cmdline = strsplit_with_quote(buf, " ", 1024);
7977         execvp(cmdline[0], cmdline);
7978
7979         perror("execvp");
7980         g_strfreev(cmdline);
7981
7982         _exit(1);
7983 }
7984
7985 static gboolean compose_ext_editor_kill(Compose *compose)
7986 {
7987         pid_t pgid = compose->exteditor_pid * -1;
7988         gint ret;
7989
7990         ret = kill(pgid, 0);
7991
7992         if (ret == 0 || (ret == -1 && EPERM == errno)) {
7993                 AlertValue val;
7994                 gchar *msg;
7995
7996                 msg = g_strdup_printf
7997                         (_("The external editor is still working.\n"
7998                            "Force terminating the process?\n"
7999                            "process group id: %d"), -pgid);
8000                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8001                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8002                         
8003                 g_free(msg);
8004
8005                 if (val == G_ALERTALTERNATE) {
8006                         g_source_remove(compose->exteditor_tag);
8007                         g_io_channel_shutdown(compose->exteditor_ch,
8008                                               FALSE, NULL);
8009                         g_io_channel_unref(compose->exteditor_ch);
8010
8011                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8012                         waitpid(compose->exteditor_pid, NULL, 0);
8013
8014                         g_warning("Terminated process group id: %d", -pgid);
8015                         g_warning("Temporary file: %s",
8016                                   compose->exteditor_file);
8017
8018                         compose_set_ext_editor_sensitive(compose, TRUE);
8019
8020                         g_free(compose->exteditor_file);
8021                         compose->exteditor_file    = NULL;
8022                         compose->exteditor_pid     = -1;
8023                         compose->exteditor_ch      = NULL;
8024                         compose->exteditor_tag     = -1;
8025                 } else
8026                         return FALSE;
8027         }
8028
8029         return TRUE;
8030 }
8031
8032 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8033                                  gpointer data)
8034 {
8035         gchar buf[3] = "3";
8036         Compose *compose = (Compose *)data;
8037         gsize bytes_read;
8038
8039         debug_print(_("Compose: input from monitoring process\n"));
8040
8041         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8042
8043         g_io_channel_shutdown(source, FALSE, NULL);
8044         g_io_channel_unref(source);
8045
8046         waitpid(compose->exteditor_pid, NULL, 0);
8047
8048         if (buf[0] == '0') {            /* success */
8049                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8050                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8051
8052                 gtk_text_buffer_set_text(buffer, "", -1);
8053                 compose_insert_file(compose, compose->exteditor_file);
8054                 compose_changed_cb(NULL, compose);
8055
8056                 if (g_unlink(compose->exteditor_file) < 0)
8057                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8058         } else if (buf[0] == '1') {     /* failed */
8059                 g_warning("Couldn't exec external editor\n");
8060                 if (g_unlink(compose->exteditor_file) < 0)
8061                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8062         } else if (buf[0] == '2') {
8063                 g_warning("Couldn't write to file\n");
8064         } else if (buf[0] == '3') {
8065                 g_warning("Pipe read failed\n");
8066         }
8067
8068         compose_set_ext_editor_sensitive(compose, TRUE);
8069
8070         g_free(compose->exteditor_file);
8071         compose->exteditor_file    = NULL;
8072         compose->exteditor_pid     = -1;
8073         compose->exteditor_ch      = NULL;
8074         compose->exteditor_tag     = -1;
8075
8076         return FALSE;
8077 }
8078
8079 static void compose_set_ext_editor_sensitive(Compose *compose,
8080                                              gboolean sensitive)
8081 {
8082         GtkItemFactory *ifactory;
8083
8084         ifactory = gtk_item_factory_from_widget(compose->menubar);
8085
8086         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
8087         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
8088         menu_set_sensitive(ifactory, "/Message/Insert file", sensitive);
8089         menu_set_sensitive(ifactory, "/Message/Insert signature", sensitive);
8090         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
8091         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
8092         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
8093                            sensitive);
8094
8095         gtk_widget_set_sensitive(compose->text,                       sensitive);
8096         if (compose->toolbar->send_btn)
8097                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8098         if (compose->toolbar->sendl_btn)
8099                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8100         if (compose->toolbar->draft_btn)
8101                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8102         if (compose->toolbar->insert_btn)
8103                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8104         if (compose->toolbar->sig_btn)
8105                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8106         if (compose->toolbar->exteditor_btn)
8107                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8108         if (compose->toolbar->linewrap_current_btn)
8109                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8110         if (compose->toolbar->linewrap_all_btn)
8111                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8112 }
8113 #endif /* G_OS_UNIX */
8114
8115 /**
8116  * compose_undo_state_changed:
8117  *
8118  * Change the sensivity of the menuentries undo and redo
8119  **/
8120 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8121                                        gint redo_state, gpointer data)
8122 {
8123         GtkWidget *widget = GTK_WIDGET(data);
8124         GtkItemFactory *ifactory;
8125
8126         g_return_if_fail(widget != NULL);
8127
8128         ifactory = gtk_item_factory_from_widget(widget);
8129
8130         switch (undo_state) {
8131         case UNDO_STATE_TRUE:
8132                 if (!undostruct->undo_state) {
8133                         undostruct->undo_state = TRUE;
8134                         menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
8135                 }
8136                 break;
8137         case UNDO_STATE_FALSE:
8138                 if (undostruct->undo_state) {
8139                         undostruct->undo_state = FALSE;
8140                         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
8141                 }
8142                 break;
8143         case UNDO_STATE_UNCHANGED:
8144                 break;
8145         case UNDO_STATE_REFRESH:
8146                 menu_set_sensitive(ifactory, "/Edit/Undo",
8147                                    undostruct->undo_state);
8148                 break;
8149         default:
8150                 g_warning("Undo state not recognized");
8151                 break;
8152         }
8153
8154         switch (redo_state) {
8155         case UNDO_STATE_TRUE:
8156                 if (!undostruct->redo_state) {
8157                         undostruct->redo_state = TRUE;
8158                         menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
8159                 }
8160                 break;
8161         case UNDO_STATE_FALSE:
8162                 if (undostruct->redo_state) {
8163                         undostruct->redo_state = FALSE;
8164                         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
8165                 }
8166                 break;
8167         case UNDO_STATE_UNCHANGED:
8168                 break;
8169         case UNDO_STATE_REFRESH:
8170                 menu_set_sensitive(ifactory, "/Edit/Redo",
8171                                    undostruct->redo_state);
8172                 break;
8173         default:
8174                 g_warning("Redo state not recognized");
8175                 break;
8176         }
8177 }
8178
8179 /* callback functions */
8180
8181 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8182  * includes "non-client" (windows-izm) in calculation, so this calculation
8183  * may not be accurate.
8184  */
8185 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8186                                         GtkAllocation *allocation,
8187                                         GtkSHRuler *shruler)
8188 {
8189         if (prefs_common.show_ruler) {
8190                 gint char_width = 0, char_height = 0;
8191                 gint line_width_in_chars;
8192
8193                 gtkut_get_font_size(GTK_WIDGET(widget),
8194                                     &char_width, &char_height);
8195                 line_width_in_chars =
8196                         (allocation->width - allocation->x) / char_width;
8197
8198                 /* got the maximum */
8199                 gtk_ruler_set_range(GTK_RULER(shruler),
8200                                     0.0, line_width_in_chars, 0,
8201                                     /*line_width_in_chars*/ char_width);
8202         }
8203
8204         return TRUE;
8205 }
8206
8207 static void account_activated(GtkComboBox *optmenu, gpointer data)
8208 {
8209         Compose *compose = (Compose *)data;
8210
8211         PrefsAccount *ac;
8212         gchar *folderidentifier;
8213         gint account_id = 0;
8214         GtkTreeModel *menu;
8215         GtkTreeIter iter;
8216
8217         /* Get ID of active account in the combo box */
8218         menu = gtk_combo_box_get_model(optmenu);
8219         gtk_combo_box_get_active_iter(optmenu, &iter);
8220         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8221
8222         ac = account_find_from_id(account_id);
8223         g_return_if_fail(ac != NULL);
8224
8225         if (ac != compose->account)
8226                 compose_select_account(compose, ac, FALSE);
8227
8228         /* Set message save folder */
8229         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8230                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8231         }
8232         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8233                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8234                            
8235         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8236         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8237                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8238                                   (compose->account, F_OUTBOX));
8239                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8240                 g_free(folderidentifier);
8241         }
8242 }
8243
8244 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8245                             GtkTreeViewColumn *column, Compose *compose)
8246 {
8247         compose_attach_property(compose);
8248 }
8249
8250 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8251                                       gpointer data)
8252 {
8253         Compose *compose = (Compose *)data;
8254         GtkTreeSelection *attach_selection;
8255         gint attach_nr_selected;
8256         GtkItemFactory *ifactory;
8257         
8258         if (!event) return FALSE;
8259
8260         if (event->button == 3) {
8261                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8262                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8263                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
8264                         
8265                 if (attach_nr_selected > 0)
8266                 {
8267                         menu_set_sensitive(ifactory, "/Remove", TRUE);
8268                         menu_set_sensitive(ifactory, "/Properties...", TRUE);
8269                 } else {
8270                         menu_set_sensitive(ifactory, "/Remove", FALSE);
8271                         menu_set_sensitive(ifactory, "/Properties...", FALSE);
8272                 }
8273                         
8274                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8275                                NULL, NULL, event->button, event->time);
8276                 return TRUE;                           
8277         }
8278
8279         return FALSE;
8280 }
8281
8282 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8283                                    gpointer data)
8284 {
8285         Compose *compose = (Compose *)data;
8286
8287         if (!event) return FALSE;
8288
8289         switch (event->keyval) {
8290         case GDK_Delete:
8291                 compose_attach_remove_selected(compose);
8292                 break;
8293         }
8294         return FALSE;
8295 }
8296
8297 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8298 {
8299         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
8300         toolbar_comp_set_sensitive(compose, allow);
8301         menu_set_sensitive(ifactory, "/Message", allow);
8302         menu_set_sensitive(ifactory, "/Edit", allow);
8303 #if USE_ASPELL
8304         menu_set_sensitive(ifactory, "/Spelling", allow);
8305 #endif  
8306         menu_set_sensitive(ifactory, "/Options", allow);
8307         menu_set_sensitive(ifactory, "/Tools", allow);
8308         menu_set_sensitive(ifactory, "/Help", allow);
8309         
8310         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8311
8312 }
8313
8314 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
8315 {
8316         Compose *compose = (Compose *)data;
8317         
8318         if (prefs_common.work_offline && 
8319             !inc_offline_should_override(TRUE,
8320                 _("Claws Mail needs network access in order "
8321                   "to send this email.")))
8322                 return;
8323         
8324         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8325                 g_source_remove(compose->draft_timeout_tag);
8326                 compose->draft_timeout_tag = -1;
8327         }
8328
8329         compose_send(compose);
8330 }
8331
8332 static void compose_send_later_cb(gpointer data, guint action,
8333                                   GtkWidget *widget)
8334 {
8335         Compose *compose = (Compose *)data;
8336         gint val;
8337
8338         inc_lock();
8339         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8340         inc_unlock();
8341
8342         if (!val) {
8343                 compose_close(compose);
8344         } else if (val == -1) {
8345                 alertpanel_error(_("Could not queue message."));
8346         } else if (val == -2) {
8347                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8348         } else if (val == -3) {
8349                 if (privacy_peek_error())
8350                 alertpanel_error(_("Could not queue message for sending:\n\n"
8351                                    "Signature failed: %s"), privacy_get_error());
8352         } else if (val == -4) {
8353                 alertpanel_error(_("Could not queue message for sending:\n\n"
8354                                    "Charset conversion failed."));
8355         } else if (val == -5) {
8356                 alertpanel_error(_("Could not queue message for sending:\n\n"
8357                                    "Couldn't get recipient encryption key."));
8358         } else if (val == -6) {
8359                 /* silent error */
8360         }
8361         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8362 }
8363
8364 #define DRAFTED_AT_EXIT "drafted_at_exit"
8365 static void compose_register_draft(MsgInfo *info)
8366 {
8367         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8368                                       DRAFTED_AT_EXIT, NULL);
8369         FILE *fp = fopen(filepath, "ab");
8370         
8371         if (fp) {
8372                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8373                                 info->msgnum);
8374                 fclose(fp);
8375         }
8376                 
8377         g_free(filepath);       
8378 }
8379
8380 gboolean compose_draft (gpointer data, guint action) 
8381 {
8382         Compose *compose = (Compose *)data;
8383         FolderItem *draft;
8384         gchar *tmp;
8385         gint msgnum;
8386         MsgFlags flag = {0, 0};
8387         static gboolean lock = FALSE;
8388         MsgInfo *newmsginfo;
8389         FILE *fp;
8390         gboolean target_locked = FALSE;
8391         
8392         if (lock) return FALSE;
8393
8394         if (compose->sending)
8395                 return TRUE;
8396
8397         draft = account_get_special_folder(compose->account, F_DRAFT);
8398         g_return_val_if_fail(draft != NULL, FALSE);
8399         
8400         if (!g_mutex_trylock(compose->mutex)) {
8401                 /* we don't want to lock the mutex once it's available,
8402                  * because as the only other part of compose.c locking
8403                  * it is compose_close - which means once unlocked,
8404                  * the compose struct will be freed */
8405                 debug_print("couldn't lock mutex, probably sending\n");
8406                 return FALSE;
8407         }
8408         
8409         lock = TRUE;
8410
8411         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8412                               G_DIR_SEPARATOR, compose);
8413         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8414                 FILE_OP_ERROR(tmp, "fopen");
8415                 goto unlock;
8416         }
8417
8418         /* chmod for security */
8419         if (change_file_mode_rw(fp, tmp) < 0) {
8420                 FILE_OP_ERROR(tmp, "chmod");
8421                 g_warning("can't change file mode\n");
8422         }
8423
8424         /* Save draft infos */
8425         fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id);
8426         fprintf(fp, "S:%s\n", compose->account->address);
8427
8428         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8429                 gchar *savefolderid;
8430
8431                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8432                 fprintf(fp, "SCF:%s\n", savefolderid);
8433                 g_free(savefolderid);
8434         }
8435         if (compose->return_receipt) {
8436                 fprintf(fp, "RRCPT:1\n");
8437         }
8438         if (compose->privacy_system) {
8439                 fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing);
8440                 fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption);
8441                 fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system);
8442         }
8443
8444         /* Message-ID of message replying to */
8445         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8446                 gchar *folderid;
8447                 
8448                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8449                 fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid);
8450                 g_free(folderid);
8451         }
8452         /* Message-ID of message forwarding to */
8453         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8454                 gchar *folderid;
8455                 
8456                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8457                 fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid);
8458                 g_free(folderid);
8459         }
8460
8461         /* end of headers */
8462         fprintf(fp, "X-Claws-End-Special-Headers: 1\n");
8463
8464         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8465                 fclose(fp);
8466                 g_unlink(tmp);
8467                 g_free(tmp);
8468                 goto unlock;
8469         }
8470         fclose(fp);
8471         
8472         if (compose->targetinfo) {
8473                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8474                 flag.perm_flags = target_locked?MSG_LOCKED:0;
8475         }
8476         flag.tmp_flags = MSG_DRAFT;
8477
8478         folder_item_scan(draft);
8479         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8480                 MsgInfo *tmpinfo = NULL;
8481                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8482                 if (compose->msgid) {
8483                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8484                 }
8485                 if (tmpinfo) {
8486                         msgnum = tmpinfo->msgnum;
8487                         procmsg_msginfo_free(tmpinfo);
8488                         debug_print("got draft msgnum %d from scanning\n", msgnum);
8489                 } else {
8490                         debug_print("didn't get draft msgnum after scanning\n");
8491                 }
8492         } else {
8493                 debug_print("got draft msgnum %d from adding\n", msgnum);
8494         }
8495         if (msgnum < 0) {
8496                 g_unlink(tmp);
8497                 g_free(tmp);
8498                 if (action != COMPOSE_AUTO_SAVE) {
8499                         if (action != COMPOSE_DRAFT_FOR_EXIT)
8500                                 alertpanel_error(_("Could not save draft."));
8501                         else {
8502                                 AlertValue val;
8503                                 gtkut_window_popup(compose->window);
8504                                 val = alertpanel_full(_("Could not save draft"),
8505                                         _("Could not save draft.\n"
8506                                         "Do you want to cancel exit or discard this email?"),
8507                                           _("_Cancel exit"), _("_Discard email"), NULL,
8508                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8509                                 if (val == G_ALERTALTERNATE) {
8510                                         lock = FALSE;
8511                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8512                                         compose_close(compose);
8513                                         return TRUE;
8514                                 } else {
8515                                         lock = FALSE;
8516                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8517                                         return FALSE;
8518                                 }
8519                         }
8520                 }
8521                 goto unlock;
8522         }
8523         g_free(tmp);
8524
8525         if (compose->mode == COMPOSE_REEDIT) {
8526                 compose_remove_reedit_target(compose, TRUE);
8527         }
8528
8529         newmsginfo = folder_item_get_msginfo(draft, msgnum);
8530
8531         if (newmsginfo) {
8532                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8533                 if (target_locked)
8534                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8535                 else
8536                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8537                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8538                         procmsg_msginfo_set_flags(newmsginfo, 0,
8539                                                   MSG_HAS_ATTACHMENT);
8540
8541                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8542                         compose_register_draft(newmsginfo);
8543                 }
8544                 procmsg_msginfo_free(newmsginfo);
8545         }
8546         
8547         folder_item_scan(draft);
8548         
8549         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8550                 lock = FALSE;
8551                 g_mutex_unlock(compose->mutex); /* must be done before closing */
8552                 compose_close(compose);
8553                 return TRUE;
8554         } else {
8555                 struct stat s;
8556                 gchar *path;
8557
8558                 path = folder_item_fetch_msg(draft, msgnum);
8559                 if (path == NULL) {
8560                         debug_print("can't fetch %s:%d\n",draft->path, msgnum);
8561                         goto unlock;
8562                 }
8563                 if (g_stat(path, &s) < 0) {
8564                         FILE_OP_ERROR(path, "stat");
8565                         g_free(path);
8566                         goto unlock;
8567                 }
8568                 g_free(path);
8569
8570                 procmsg_msginfo_free(compose->targetinfo);
8571                 compose->targetinfo = procmsg_msginfo_new();
8572                 compose->targetinfo->msgnum = msgnum;
8573                 compose->targetinfo->size = s.st_size;
8574                 compose->targetinfo->mtime = s.st_mtime;
8575                 compose->targetinfo->folder = draft;
8576                 if (target_locked)
8577                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
8578                 compose->mode = COMPOSE_REEDIT;
8579                 
8580                 if (action == COMPOSE_AUTO_SAVE) {
8581                         compose->autosaved_draft = compose->targetinfo;
8582                 }
8583                 compose->modified = FALSE;
8584                 compose_set_title(compose);
8585         }
8586 unlock:
8587         lock = FALSE;
8588         g_mutex_unlock(compose->mutex);
8589         return TRUE;
8590 }
8591
8592 void compose_clear_exit_drafts(void)
8593 {
8594         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8595                                       DRAFTED_AT_EXIT, NULL);
8596         if (is_file_exist(filepath))
8597                 g_unlink(filepath);
8598         
8599         g_free(filepath);
8600 }
8601
8602 void compose_reopen_exit_drafts(void)
8603 {
8604         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8605                                       DRAFTED_AT_EXIT, NULL);
8606         FILE *fp = fopen(filepath, "rb");
8607         gchar buf[1024];
8608         
8609         if (fp) {
8610                 while (fgets(buf, sizeof(buf), fp)) {
8611                         gchar **parts = g_strsplit(buf, "\t", 2);
8612                         const gchar *folder = parts[0];
8613                         int msgnum = parts[1] ? atoi(parts[1]):-1;
8614                         
8615                         if (folder && *folder && msgnum > -1) {
8616                                 FolderItem *item = folder_find_item_from_identifier(folder);
8617                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
8618                                 if (info)
8619                                         compose_reedit(info, FALSE);
8620                         }
8621                         g_strfreev(parts);
8622                 }       
8623                 fclose(fp);
8624         }       
8625         g_free(filepath);
8626         compose_clear_exit_drafts();
8627 }
8628
8629 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
8630 {
8631         compose_draft(data, action);
8632 }
8633
8634 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
8635 {
8636         Compose *compose = (Compose *)data;
8637         GList *file_list;
8638
8639         if (compose->redirect_filename != NULL)
8640                 return;
8641
8642         file_list = filesel_select_multiple_files_open(_("Select file"));
8643
8644         if (file_list) {
8645                 GList *tmp;
8646
8647                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8648                         gchar *file = (gchar *) tmp->data;
8649                         gchar *utf8_filename = conv_filename_to_utf8(file);
8650                         compose_attach_append(compose, file, utf8_filename, NULL);
8651                         compose_changed_cb(NULL, compose);
8652                         g_free(file);
8653                         g_free(utf8_filename);
8654                 }
8655                 g_list_free(file_list);
8656         }               
8657 }
8658
8659 static void compose_insert_file_cb(gpointer data, guint action,
8660                                    GtkWidget *widget)
8661 {
8662         Compose *compose = (Compose *)data;
8663         GList *file_list;
8664
8665         file_list = filesel_select_multiple_files_open(_("Select file"));
8666
8667         if (file_list) {
8668                 GList *tmp;
8669
8670                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8671                         gchar *file = (gchar *) tmp->data;
8672                         gchar *filedup = g_strdup(file);
8673                         gchar *shortfile = g_path_get_basename(filedup);
8674                         ComposeInsertResult res;
8675
8676                         res = compose_insert_file(compose, file);
8677                         if (res == COMPOSE_INSERT_READ_ERROR) {
8678                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
8679                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
8680                                 alertpanel_error(_("File '%s' contained invalid characters\n"
8681                                                    "for the current encoding, insertion may be incorrect."), shortfile);
8682                         }
8683                         g_free(shortfile);
8684                         g_free(filedup);
8685                         g_free(file);
8686                 }
8687                 g_list_free(file_list);
8688         }
8689 }
8690
8691 static void compose_insert_sig_cb(gpointer data, guint action,
8692                                   GtkWidget *widget)
8693 {
8694         Compose *compose = (Compose *)data;
8695
8696         compose_insert_sig(compose, FALSE);
8697 }
8698
8699 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
8700                               gpointer data)
8701 {
8702         gint x, y;
8703         Compose *compose = (Compose *)data;
8704
8705         gtkut_widget_get_uposition(widget, &x, &y);
8706         prefs_common.compose_x = x;
8707         prefs_common.compose_y = y;
8708
8709         if (compose->sending || compose->updating)
8710                 return TRUE;
8711         compose_close_cb(compose, 0, NULL);
8712         return TRUE;
8713 }
8714
8715 void compose_close_toolbar(Compose *compose)
8716 {
8717         compose_close_cb(compose, 0, NULL);
8718 }
8719
8720 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
8721 {
8722         Compose *compose = (Compose *)data;
8723         AlertValue val;
8724
8725 #ifdef G_OS_UNIX
8726         if (compose->exteditor_tag != -1) {
8727                 if (!compose_ext_editor_kill(compose))
8728                         return;
8729         }
8730 #endif
8731
8732         if (compose->modified) {
8733                 val = alertpanel(_("Discard message"),
8734                                  _("This message has been modified. Discard it?"),
8735                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
8736
8737                 switch (val) {
8738                 case G_ALERTDEFAULT:
8739                         if (prefs_common.autosave)
8740                                 compose_remove_draft(compose);                  
8741                         break;
8742                 case G_ALERTALTERNATE:
8743                         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);
8744                         return;
8745                 default:
8746                         return;
8747                 }
8748         }
8749
8750         compose_close(compose);
8751 }
8752
8753 static void compose_set_encoding_cb(gpointer data, guint action,
8754                                     GtkWidget *widget)
8755 {
8756         Compose *compose = (Compose *)data;
8757
8758         if (GTK_CHECK_MENU_ITEM(widget)->active)
8759                 compose->out_encoding = (CharSet)action;
8760 }
8761
8762 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
8763 {
8764         Compose *compose = (Compose *)data;
8765
8766         addressbook_open(compose);
8767 }
8768
8769 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
8770 {
8771         Compose *compose = (Compose *)data;
8772         Template *tmpl;
8773         gchar *msg;
8774         AlertValue val;
8775
8776         tmpl = g_object_get_data(G_OBJECT(widget), "template");
8777         g_return_if_fail(tmpl != NULL);
8778
8779         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
8780                               tmpl->name);
8781         val = alertpanel(_("Apply template"), msg,
8782                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
8783         g_free(msg);
8784
8785         if (val == G_ALERTDEFAULT)
8786                 compose_template_apply(compose, tmpl, TRUE);
8787         else if (val == G_ALERTALTERNATE)
8788                 compose_template_apply(compose, tmpl, FALSE);
8789 }
8790
8791 static void compose_ext_editor_cb(gpointer data, guint action,
8792                                   GtkWidget *widget)
8793 {
8794         Compose *compose = (Compose *)data;
8795
8796         compose_exec_ext_editor(compose);
8797 }
8798
8799 static void compose_undo_cb(Compose *compose)
8800 {
8801         gboolean prev_autowrap = compose->autowrap;
8802
8803         compose->autowrap = FALSE;
8804         undo_undo(compose->undostruct);
8805         compose->autowrap = prev_autowrap;
8806 }
8807
8808 static void compose_redo_cb(Compose *compose)
8809 {
8810         gboolean prev_autowrap = compose->autowrap;
8811         
8812         compose->autowrap = FALSE;
8813         undo_redo(compose->undostruct);
8814         compose->autowrap = prev_autowrap;
8815 }
8816
8817 static void entry_cut_clipboard(GtkWidget *entry)
8818 {
8819         if (GTK_IS_EDITABLE(entry))
8820                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
8821         else if (GTK_IS_TEXT_VIEW(entry))
8822                 gtk_text_buffer_cut_clipboard(
8823                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
8824                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
8825                         TRUE);
8826 }
8827
8828 static void entry_copy_clipboard(GtkWidget *entry)
8829 {
8830         if (GTK_IS_EDITABLE(entry))
8831                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
8832         else if (GTK_IS_TEXT_VIEW(entry))
8833                 gtk_text_buffer_copy_clipboard(
8834                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
8835                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
8836 }
8837
8838 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
8839                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
8840 {
8841         if (GTK_IS_TEXT_VIEW(entry)) {
8842                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
8843                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
8844                 GtkTextIter start_iter, end_iter;
8845                 gint start, end;
8846                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
8847
8848                 if (contents == NULL)
8849                         return;
8850
8851                 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
8852
8853                 /* we shouldn't delete the selection when middle-click-pasting, or we
8854                  * can't mid-click-paste our own selection */
8855                 if (clip != GDK_SELECTION_PRIMARY) {
8856                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
8857                 }
8858                 
8859                 if (insert_place == NULL) {
8860                         /* if insert_place isn't specified, insert at the cursor.
8861                          * used for Ctrl-V pasting */
8862                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
8863                         start = gtk_text_iter_get_offset(&start_iter);
8864                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
8865                 } else {
8866                         /* if insert_place is specified, paste here.
8867                          * used for mid-click-pasting */
8868                         start = gtk_text_iter_get_offset(insert_place);
8869                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
8870                 }
8871                 
8872                 if (!wrap) {
8873                         /* paste unwrapped: mark the paste so it's not wrapped later */
8874                         end = start + strlen(contents);
8875                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
8876                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
8877                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
8878                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
8879                         /* rewrap paragraph now (after a mid-click-paste) */
8880                         mark_start = gtk_text_buffer_get_insert(buffer);
8881                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
8882                         gtk_text_iter_backward_char(&start_iter);
8883                         compose_beautify_paragraph(compose, &start_iter, TRUE);
8884                 }
8885         } else if (GTK_IS_EDITABLE(entry))
8886                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
8887         
8888 }
8889
8890 static void entry_allsel(GtkWidget *entry)
8891 {
8892         if (GTK_IS_EDITABLE(entry))
8893                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
8894         else if (GTK_IS_TEXT_VIEW(entry)) {
8895                 GtkTextIter startiter, enditer;
8896                 GtkTextBuffer *textbuf;
8897
8898                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
8899                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
8900                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
8901
8902                 gtk_text_buffer_move_mark_by_name(textbuf, 
8903                         "selection_bound", &startiter);
8904                 gtk_text_buffer_move_mark_by_name(textbuf, 
8905                         "insert", &enditer);
8906         }
8907 }
8908
8909 static void compose_cut_cb(Compose *compose)
8910 {
8911         if (compose->focused_editable 
8912 #ifndef MAEMO
8913             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8914 #endif
8915             )
8916                 entry_cut_clipboard(compose->focused_editable);
8917 }
8918
8919 static void compose_copy_cb(Compose *compose)
8920 {
8921         if (compose->focused_editable 
8922 #ifndef MAEMO
8923             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8924 #endif
8925             )
8926                 entry_copy_clipboard(compose->focused_editable);
8927 }
8928
8929 static void compose_paste_cb(Compose *compose)
8930 {
8931         gint prev_autowrap;
8932         GtkTextBuffer *buffer;
8933         BLOCK_WRAP();
8934         if (compose->focused_editable &&
8935             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
8936                 entry_paste_clipboard(compose, compose->focused_editable, 
8937                                 prefs_common.linewrap_pastes,
8938                                 GDK_SELECTION_CLIPBOARD, NULL);
8939         UNBLOCK_WRAP();
8940 }
8941
8942 static void compose_paste_as_quote_cb(Compose *compose)
8943 {
8944         gint wrap_quote = prefs_common.linewrap_quote;
8945         if (compose->focused_editable 
8946 #ifndef MAEMO
8947             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8948 #endif
8949             ) {
8950                 /* let text_insert() (called directly or at a later time
8951                  * after the gtk_editable_paste_clipboard) know that 
8952                  * text is to be inserted as a quotation. implemented
8953                  * by using a simple refcount... */
8954                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
8955                                                 G_OBJECT(compose->focused_editable),
8956                                                 "paste_as_quotation"));
8957                 g_object_set_data(G_OBJECT(compose->focused_editable),
8958                                     "paste_as_quotation",
8959                                     GINT_TO_POINTER(paste_as_quotation + 1));
8960                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
8961                 entry_paste_clipboard(compose, compose->focused_editable, 
8962                                 prefs_common.linewrap_pastes,
8963                                 GDK_SELECTION_CLIPBOARD, NULL);
8964                 prefs_common.linewrap_quote = wrap_quote;
8965         }
8966 }
8967
8968 static void compose_paste_no_wrap_cb(Compose *compose)
8969 {
8970         gint prev_autowrap;
8971         GtkTextBuffer *buffer;
8972         BLOCK_WRAP();
8973         if (compose->focused_editable 
8974 #ifndef MAEMO
8975             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8976 #endif
8977             )
8978                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
8979                         GDK_SELECTION_CLIPBOARD, NULL);
8980         UNBLOCK_WRAP();
8981 }
8982
8983 static void compose_paste_wrap_cb(Compose *compose)
8984 {
8985         gint prev_autowrap;
8986         GtkTextBuffer *buffer;
8987         BLOCK_WRAP();
8988         if (compose->focused_editable 
8989 #ifndef MAEMO
8990             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8991 #endif
8992             )
8993                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
8994                         GDK_SELECTION_CLIPBOARD, NULL);
8995         UNBLOCK_WRAP();
8996 }
8997
8998 static void compose_allsel_cb(Compose *compose)
8999 {
9000         if (compose->focused_editable 
9001 #ifndef MAEMO
9002             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9003 #endif
9004             )
9005                 entry_allsel(compose->focused_editable);
9006 }
9007
9008 static void textview_move_beginning_of_line (GtkTextView *text)
9009 {
9010         GtkTextBuffer *buffer;
9011         GtkTextMark *mark;
9012         GtkTextIter ins;
9013
9014         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9015
9016         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9017         mark = gtk_text_buffer_get_insert(buffer);
9018         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9019         gtk_text_iter_set_line_offset(&ins, 0);
9020         gtk_text_buffer_place_cursor(buffer, &ins);
9021 }
9022
9023 static void textview_move_forward_character (GtkTextView *text)
9024 {
9025         GtkTextBuffer *buffer;
9026         GtkTextMark *mark;
9027         GtkTextIter ins;
9028
9029         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9030
9031         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9032         mark = gtk_text_buffer_get_insert(buffer);
9033         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9034         if (gtk_text_iter_forward_cursor_position(&ins))
9035                 gtk_text_buffer_place_cursor(buffer, &ins);
9036 }
9037
9038 static void textview_move_backward_character (GtkTextView *text)
9039 {
9040         GtkTextBuffer *buffer;
9041         GtkTextMark *mark;
9042         GtkTextIter ins;
9043
9044         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9045
9046         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9047         mark = gtk_text_buffer_get_insert(buffer);
9048         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9049         if (gtk_text_iter_backward_cursor_position(&ins))
9050                 gtk_text_buffer_place_cursor(buffer, &ins);
9051 }
9052
9053 static void textview_move_forward_word (GtkTextView *text)
9054 {
9055         GtkTextBuffer *buffer;
9056         GtkTextMark *mark;
9057         GtkTextIter ins;
9058         gint count;
9059
9060         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9061
9062         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9063         mark = gtk_text_buffer_get_insert(buffer);
9064         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9065         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9066         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9067                 gtk_text_iter_backward_word_start(&ins);
9068                 gtk_text_buffer_place_cursor(buffer, &ins);
9069         }
9070 }
9071
9072 static void textview_move_backward_word (GtkTextView *text)
9073 {
9074         GtkTextBuffer *buffer;
9075         GtkTextMark *mark;
9076         GtkTextIter ins;
9077         gint count;
9078
9079         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9080
9081         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9082         mark = gtk_text_buffer_get_insert(buffer);
9083         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9084         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9085         if (gtk_text_iter_backward_word_starts(&ins, 1))
9086                 gtk_text_buffer_place_cursor(buffer, &ins);
9087 }
9088
9089 static void textview_move_end_of_line (GtkTextView *text)
9090 {
9091         GtkTextBuffer *buffer;
9092         GtkTextMark *mark;
9093         GtkTextIter ins;
9094
9095         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9096
9097         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9098         mark = gtk_text_buffer_get_insert(buffer);
9099         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9100         if (gtk_text_iter_forward_to_line_end(&ins))
9101                 gtk_text_buffer_place_cursor(buffer, &ins);
9102 }
9103
9104 static void textview_move_next_line (GtkTextView *text)
9105 {
9106         GtkTextBuffer *buffer;
9107         GtkTextMark *mark;
9108         GtkTextIter ins;
9109         gint offset;
9110
9111         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9112
9113         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9114         mark = gtk_text_buffer_get_insert(buffer);
9115         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9116         offset = gtk_text_iter_get_line_offset(&ins);
9117         if (gtk_text_iter_forward_line(&ins)) {
9118                 gtk_text_iter_set_line_offset(&ins, offset);
9119                 gtk_text_buffer_place_cursor(buffer, &ins);
9120         }
9121 }
9122
9123 static void textview_move_previous_line (GtkTextView *text)
9124 {
9125         GtkTextBuffer *buffer;
9126         GtkTextMark *mark;
9127         GtkTextIter ins;
9128         gint offset;
9129
9130         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9131
9132         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9133         mark = gtk_text_buffer_get_insert(buffer);
9134         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9135         offset = gtk_text_iter_get_line_offset(&ins);
9136         if (gtk_text_iter_backward_line(&ins)) {
9137                 gtk_text_iter_set_line_offset(&ins, offset);
9138                 gtk_text_buffer_place_cursor(buffer, &ins);
9139         }
9140 }
9141
9142 static void textview_delete_forward_character (GtkTextView *text)
9143 {
9144         GtkTextBuffer *buffer;
9145         GtkTextMark *mark;
9146         GtkTextIter ins, end_iter;
9147
9148         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9149
9150         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9151         mark = gtk_text_buffer_get_insert(buffer);
9152         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9153         end_iter = ins;
9154         if (gtk_text_iter_forward_char(&end_iter)) {
9155                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9156         }
9157 }
9158
9159 static void textview_delete_backward_character (GtkTextView *text)
9160 {
9161         GtkTextBuffer *buffer;
9162         GtkTextMark *mark;
9163         GtkTextIter ins, end_iter;
9164
9165         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9166
9167         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9168         mark = gtk_text_buffer_get_insert(buffer);
9169         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9170         end_iter = ins;
9171         if (gtk_text_iter_backward_char(&end_iter)) {
9172                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9173         }
9174 }
9175
9176 static void textview_delete_forward_word (GtkTextView *text)
9177 {
9178         GtkTextBuffer *buffer;
9179         GtkTextMark *mark;
9180         GtkTextIter ins, end_iter;
9181
9182         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9183
9184         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9185         mark = gtk_text_buffer_get_insert(buffer);
9186         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9187         end_iter = ins;
9188         if (gtk_text_iter_forward_word_end(&end_iter)) {
9189                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9190         }
9191 }
9192
9193 static void textview_delete_backward_word (GtkTextView *text)
9194 {
9195         GtkTextBuffer *buffer;
9196         GtkTextMark *mark;
9197         GtkTextIter ins, end_iter;
9198
9199         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9200
9201         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9202         mark = gtk_text_buffer_get_insert(buffer);
9203         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9204         end_iter = ins;
9205         if (gtk_text_iter_backward_word_start(&end_iter)) {
9206                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9207         }
9208 }
9209
9210 static void textview_delete_line (GtkTextView *text)
9211 {
9212         GtkTextBuffer *buffer;
9213         GtkTextMark *mark;
9214         GtkTextIter ins, start_iter, end_iter;
9215         gboolean found;
9216
9217         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9218
9219         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9220         mark = gtk_text_buffer_get_insert(buffer);
9221         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9222
9223         start_iter = ins;
9224         gtk_text_iter_set_line_offset(&start_iter, 0);
9225
9226         end_iter = ins;
9227         if (gtk_text_iter_ends_line(&end_iter))
9228                 found = gtk_text_iter_forward_char(&end_iter);
9229         else
9230                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9231
9232         if (found)
9233                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9234 }
9235
9236 static void textview_delete_to_line_end (GtkTextView *text)
9237 {
9238         GtkTextBuffer *buffer;
9239         GtkTextMark *mark;
9240         GtkTextIter ins, end_iter;
9241         gboolean found;
9242
9243         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9244
9245         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9246         mark = gtk_text_buffer_get_insert(buffer);
9247         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9248         end_iter = ins;
9249         if (gtk_text_iter_ends_line(&end_iter))
9250                 found = gtk_text_iter_forward_char(&end_iter);
9251         else
9252                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9253         if (found)
9254                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9255 }
9256
9257 static void compose_advanced_action_cb(Compose *compose,
9258                                         ComposeCallAdvancedAction action)
9259 {
9260         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9261         static struct {
9262                 void (*do_action) (GtkTextView *text);
9263         } action_table[] = {
9264                 {textview_move_beginning_of_line},
9265                 {textview_move_forward_character},
9266                 {textview_move_backward_character},
9267                 {textview_move_forward_word},
9268                 {textview_move_backward_word},
9269                 {textview_move_end_of_line},
9270                 {textview_move_next_line},
9271                 {textview_move_previous_line},
9272                 {textview_delete_forward_character},
9273                 {textview_delete_backward_character},
9274                 {textview_delete_forward_word},
9275                 {textview_delete_backward_word},
9276                 {textview_delete_line},
9277                 {NULL}, /* gtk_stext_delete_line_n */
9278                 {textview_delete_to_line_end}
9279         };
9280
9281         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9282
9283         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9284             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9285                 if (action_table[action].do_action)
9286                         action_table[action].do_action(text);
9287                 else
9288                         g_warning("Not implemented yet.");
9289         }
9290 }
9291
9292 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9293 {
9294         gchar *str = NULL;
9295         
9296         if (GTK_IS_EDITABLE(widget)) {
9297                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9298                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9299                         strlen(str));
9300                 g_free(str);
9301                 if (widget->parent && widget->parent->parent
9302                  && widget->parent->parent->parent) {
9303                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9304                                 gint y = widget->allocation.y;
9305                                 gint height = widget->allocation.height;
9306                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9307                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9308
9309                                 if (y < (int)shown->value) {
9310                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9311                                 }
9312                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9313                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9314                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9315                                                         y + height - (int)shown->page_size - 1);
9316                                         } else {
9317                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9318                                                         (int)shown->upper - (int)shown->page_size - 1);
9319                                         }
9320                                 }
9321                         }
9322                 }
9323         }
9324
9325         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9326                 compose->focused_editable = widget;
9327         
9328 #ifdef MAEMO
9329         if (GTK_IS_TEXT_VIEW(widget) 
9330             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9331                 gtk_widget_ref(compose->notebook);
9332                 gtk_widget_ref(compose->edit_vbox);
9333                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9334                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9335                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9336                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9337                 gtk_widget_unref(compose->notebook);
9338                 gtk_widget_unref(compose->edit_vbox);
9339                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9340                                         G_CALLBACK(compose_grab_focus_cb),
9341                                         compose);
9342                 gtk_widget_grab_focus(widget);
9343                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9344                                         G_CALLBACK(compose_grab_focus_cb),
9345                                         compose);
9346         } else if (!GTK_IS_TEXT_VIEW(widget) 
9347                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9348                 gtk_widget_ref(compose->notebook);
9349                 gtk_widget_ref(compose->edit_vbox);
9350                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9351                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9352                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9353                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9354                 gtk_widget_unref(compose->notebook);
9355                 gtk_widget_unref(compose->edit_vbox);
9356                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9357                                         G_CALLBACK(compose_grab_focus_cb),
9358                                         compose);
9359                 gtk_widget_grab_focus(widget);
9360                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9361                                         G_CALLBACK(compose_grab_focus_cb),
9362                                         compose);
9363         }
9364 #endif
9365 }
9366
9367 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9368 {
9369         compose->modified = TRUE;
9370 #ifndef MAEMO
9371         compose_set_title(compose);
9372 #endif
9373 }
9374
9375 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
9376 {
9377         Compose *compose = (Compose *)data;
9378
9379         if (action == 1)
9380                 compose_wrap_all_full(compose, TRUE);
9381         else
9382                 compose_beautify_paragraph(compose, NULL, TRUE);
9383 }
9384
9385 static void compose_find_cb(gpointer data, guint action, GtkWidget *widget)
9386 {
9387         Compose *compose = (Compose *)data;
9388
9389         message_search_compose(compose);
9390 }
9391
9392 static void compose_toggle_autowrap_cb(gpointer data, guint action,
9393                                        GtkWidget *widget)
9394 {
9395         Compose *compose = (Compose *)data;
9396         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9397         if (compose->autowrap)
9398                 compose_wrap_all_full(compose, TRUE);
9399         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9400 }
9401
9402 static void compose_toggle_sign_cb(gpointer data, guint action,
9403                                    GtkWidget *widget)
9404 {
9405         Compose *compose = (Compose *)data;
9406
9407         if (GTK_CHECK_MENU_ITEM(widget)->active)
9408                 compose->use_signing = TRUE;
9409         else
9410                 compose->use_signing = FALSE;
9411 }
9412
9413 static void compose_toggle_encrypt_cb(gpointer data, guint action,
9414                                       GtkWidget *widget)
9415 {
9416         Compose *compose = (Compose *)data;
9417
9418         if (GTK_CHECK_MENU_ITEM(widget)->active)
9419                 compose->use_encryption = TRUE;
9420         else
9421                 compose->use_encryption = FALSE;
9422 }
9423
9424 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
9425 {
9426         g_free(compose->privacy_system);
9427
9428         compose->privacy_system = g_strdup(account->default_privacy_system);
9429         compose_update_privacy_system_menu_item(compose, warn);
9430 }
9431
9432 static void compose_toggle_ruler_cb(gpointer data, guint action,
9433                                     GtkWidget *widget)
9434 {
9435         Compose *compose = (Compose *)data;
9436
9437         if (GTK_CHECK_MENU_ITEM(widget)->active) {
9438                 gtk_widget_show(compose->ruler_hbox);
9439                 prefs_common.show_ruler = TRUE;
9440         } else {
9441                 gtk_widget_hide(compose->ruler_hbox);
9442                 gtk_widget_queue_resize(compose->edit_vbox);
9443                 prefs_common.show_ruler = FALSE;
9444         }
9445 }
9446
9447 static void compose_attach_drag_received_cb (GtkWidget          *widget,
9448                                              GdkDragContext     *context,
9449                                              gint                x,
9450                                              gint                y,
9451                                              GtkSelectionData   *data,
9452                                              guint               info,
9453                                              guint               time,
9454                                              gpointer            user_data)
9455 {
9456         Compose *compose = (Compose *)user_data;
9457         GList *list, *tmp;
9458
9459         if (gdk_atom_name(data->type) && 
9460             !strcmp(gdk_atom_name(data->type), "text/uri-list")
9461             && gtk_drag_get_source_widget(context) != 
9462                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9463                 list = uri_list_extract_filenames((const gchar *)data->data);
9464                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9465                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9466                         compose_attach_append
9467                                 (compose, (const gchar *)tmp->data,
9468                                  utf8_filename, NULL);
9469                         g_free(utf8_filename);
9470                 }
9471                 if (list) compose_changed_cb(NULL, compose);
9472                 list_free_strings(list);
9473                 g_list_free(list);
9474         } else if (gtk_drag_get_source_widget(context) 
9475                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9476                 /* comes from our summaryview */
9477                 SummaryView * summaryview = NULL;
9478                 GSList * list = NULL, *cur = NULL;
9479                 
9480                 if (mainwindow_get_mainwindow())
9481                         summaryview = mainwindow_get_mainwindow()->summaryview;
9482                 
9483                 if (summaryview)
9484                         list = summary_get_selected_msg_list(summaryview);
9485                 
9486                 for (cur = list; cur; cur = cur->next) {
9487                         MsgInfo *msginfo = (MsgInfo *)cur->data;
9488                         gchar *file = NULL;
9489                         if (msginfo)
9490                                 file = procmsg_get_message_file_full(msginfo, 
9491                                         TRUE, TRUE);
9492                         if (file) {
9493                                 compose_attach_append(compose, (const gchar *)file, 
9494                                         (const gchar *)file, "message/rfc822");
9495                                 g_free(file);
9496                         }
9497                 }
9498                 g_slist_free(list);
9499         }
9500 }
9501
9502 static gboolean compose_drag_drop(GtkWidget *widget,
9503                                   GdkDragContext *drag_context,
9504                                   gint x, gint y,
9505                                   guint time, gpointer user_data)
9506 {
9507         /* not handling this signal makes compose_insert_drag_received_cb
9508          * called twice */
9509         return TRUE;                                     
9510 }
9511
9512 static void compose_insert_drag_received_cb (GtkWidget          *widget,
9513                                              GdkDragContext     *drag_context,
9514                                              gint                x,
9515                                              gint                y,
9516                                              GtkSelectionData   *data,
9517                                              guint               info,
9518                                              guint               time,
9519                                              gpointer            user_data)
9520 {
9521         Compose *compose = (Compose *)user_data;
9522         GList *list, *tmp;
9523
9524         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
9525          * does not work */
9526         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
9527                 AlertValue val = G_ALERTDEFAULT;
9528
9529                 switch (prefs_common.compose_dnd_mode) {
9530                         case COMPOSE_DND_ASK:
9531                                 val = alertpanel_full(_("Insert or attach?"),
9532                                          _("Do you want to insert the contents of the file(s) "
9533                                            "into the message body, or attach it to the email?"),
9534                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
9535                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
9536                                 break;
9537                         case COMPOSE_DND_INSERT:
9538                                 val = G_ALERTALTERNATE;
9539                                 break;
9540                         case COMPOSE_DND_ATTACH:
9541                                 val = G_ALERTOTHER;
9542                                 break;
9543                         default:
9544                                 /* unexpected case */
9545                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
9546                 }
9547
9548                 if (val & G_ALERTDISABLE) {
9549                         val &= ~G_ALERTDISABLE;
9550                         /* remember what action to perform by default, only if we don't click Cancel */
9551                         if (val == G_ALERTALTERNATE)
9552                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
9553                         else if (val == G_ALERTOTHER)
9554                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
9555                 }
9556
9557                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
9558                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
9559                         return;
9560                 } else if (val == G_ALERTOTHER) {
9561                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
9562                         return;
9563                 } 
9564                 list = uri_list_extract_filenames((const gchar *)data->data);
9565                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9566                         compose_insert_file(compose, (const gchar *)tmp->data);
9567                 }
9568                 list_free_strings(list);
9569                 g_list_free(list);
9570                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9571                 return;
9572         } else {
9573 #if GTK_CHECK_VERSION(2, 8, 0)
9574                 /* do nothing, handled by GTK */
9575 #else
9576                 gchar *tmpfile = get_tmp_file();
9577                 str_write_to_file((const gchar *)data->data, tmpfile);
9578                 compose_insert_file(compose, tmpfile);
9579                 g_unlink(tmpfile);
9580                 g_free(tmpfile);
9581                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9582 #endif
9583                 return;
9584         }
9585         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9586 }
9587
9588 static void compose_header_drag_received_cb (GtkWidget          *widget,
9589                                              GdkDragContext     *drag_context,
9590                                              gint                x,
9591                                              gint                y,
9592                                              GtkSelectionData   *data,
9593                                              guint               info,
9594                                              guint               time,
9595                                              gpointer            user_data)
9596 {
9597         GtkEditable *entry = (GtkEditable *)user_data;
9598         gchar *email = (gchar *)data->data;
9599
9600         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
9601          * does not work */
9602
9603         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
9604                 gchar *decoded=g_new(gchar, strlen(email));
9605                 int start = 0;
9606
9607                 email += strlen("mailto:");
9608                 decode_uri(decoded, email); /* will fit */
9609                 gtk_editable_delete_text(entry, 0, -1);
9610                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
9611                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9612                 g_free(decoded);
9613                 return;
9614         }
9615         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9616 }
9617
9618 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
9619                                              GtkWidget *widget)
9620 {
9621         Compose *compose = (Compose *)data;
9622
9623         if (GTK_CHECK_MENU_ITEM(widget)->active)
9624                 compose->return_receipt = TRUE;
9625         else
9626                 compose->return_receipt = FALSE;
9627 }
9628
9629 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
9630                                              GtkWidget *widget)
9631 {
9632         Compose *compose = (Compose *)data;
9633
9634         if (GTK_CHECK_MENU_ITEM(widget)->active)
9635                 compose->remove_references = TRUE;
9636         else
9637                 compose->remove_references = FALSE;
9638 }
9639
9640 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
9641                                             GdkEventKey *event,
9642                                             ComposeHeaderEntry *headerentry)
9643 {
9644         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
9645             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
9646             !(event->state & GDK_MODIFIER_MASK) &&
9647             (event->keyval == GDK_BackSpace) &&
9648             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
9649                 gtk_container_remove
9650                         (GTK_CONTAINER(headerentry->compose->header_table),
9651                          headerentry->combo);
9652                 gtk_container_remove
9653                         (GTK_CONTAINER(headerentry->compose->header_table),
9654                          headerentry->entry);
9655                 headerentry->compose->header_list =
9656                         g_slist_remove(headerentry->compose->header_list,
9657                                        headerentry);
9658                 g_free(headerentry);
9659         } else  if (event->keyval == GDK_Tab) {
9660                 if (headerentry->compose->header_last == headerentry) {
9661                         /* Override default next focus, and give it to subject_entry
9662                          * instead of notebook tabs
9663                          */
9664                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
9665                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
9666                         return TRUE;
9667                 }
9668         }
9669         return FALSE;
9670 }
9671
9672 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
9673                                     ComposeHeaderEntry *headerentry)
9674 {
9675         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
9676                 compose_create_header_entry(headerentry->compose);
9677                 g_signal_handlers_disconnect_matched
9678                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
9679                          0, 0, NULL, NULL, headerentry);
9680                 
9681                 /* Automatically scroll down */
9682                 compose_show_first_last_header(headerentry->compose, FALSE);
9683                 
9684         }
9685         return FALSE;
9686 }
9687
9688 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
9689 {
9690         GtkAdjustment *vadj;
9691
9692         g_return_if_fail(compose);
9693         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
9694         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
9695
9696         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
9697         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
9698 }
9699
9700 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
9701                           const gchar *text, gint len, Compose *compose)
9702 {
9703         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
9704                                 (G_OBJECT(compose->text), "paste_as_quotation"));
9705         GtkTextMark *mark;
9706
9707         g_return_if_fail(text != NULL);
9708
9709         g_signal_handlers_block_by_func(G_OBJECT(buffer),
9710                                         G_CALLBACK(text_inserted),
9711                                         compose);
9712         if (paste_as_quotation) {
9713                 gchar *new_text;
9714                 const gchar *qmark;
9715
9716                 if (len < 0)
9717                         len = strlen(text);
9718
9719                 new_text = g_strndup(text, len);
9720
9721                 qmark = compose_quote_char_from_context(compose);
9722
9723                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9724                 gtk_text_buffer_place_cursor(buffer, iter);
9725
9726                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
9727                                                   _("Quote format error at line %d."));
9728                 quote_fmt_reset_vartable();
9729                 g_free(new_text);
9730                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
9731                                   GINT_TO_POINTER(paste_as_quotation - 1));
9732                                   
9733                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9734                 gtk_text_buffer_place_cursor(buffer, iter);
9735         } else {
9736                 if (strcmp(text, "\n") || compose->automatic_break
9737                 || gtk_text_iter_starts_line(iter))
9738                         gtk_text_buffer_insert(buffer, iter, text, len);
9739                 else {
9740                         debug_print("insert nowrap \\n\n");
9741                         gtk_text_buffer_insert_with_tags_by_name(buffer, 
9742                                 iter, text, len, "no_join", NULL);
9743                 }
9744         }
9745         
9746         mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9747         
9748         compose_beautify_paragraph(compose, iter, FALSE);
9749
9750         gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9751         gtk_text_buffer_delete_mark(buffer, mark);
9752
9753         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
9754                                           G_CALLBACK(text_inserted),
9755                                           compose);
9756         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
9757
9758         if (prefs_common.autosave && 
9759             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
9760             compose->draft_timeout_tag != -2 /* disabled while loading */)
9761                 compose->draft_timeout_tag = g_timeout_add
9762                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
9763 }
9764 static gint compose_defer_auto_save_draft(Compose *compose)
9765 {
9766         compose->draft_timeout_tag = -1;
9767         compose_draft_cb((gpointer)compose, COMPOSE_AUTO_SAVE, NULL);
9768         return FALSE;
9769 }
9770
9771 #if USE_ASPELL
9772 static void compose_check_all(Compose *compose)
9773 {
9774         if (compose->gtkaspell)
9775                 gtkaspell_check_all(compose->gtkaspell);
9776 }
9777
9778 static void compose_highlight_all(Compose *compose)
9779 {
9780         if (compose->gtkaspell)
9781                 gtkaspell_highlight_all(compose->gtkaspell);
9782 }
9783
9784 static void compose_check_backwards(Compose *compose)
9785 {
9786         if (compose->gtkaspell) 
9787                 gtkaspell_check_backwards(compose->gtkaspell);
9788         else {
9789                 GtkItemFactory *ifactory;
9790                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9791                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
9792                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
9793         }
9794 }
9795
9796 static void compose_check_forwards_go(Compose *compose)
9797 {
9798         if (compose->gtkaspell) 
9799                 gtkaspell_check_forwards_go(compose->gtkaspell);
9800         else {
9801                 GtkItemFactory *ifactory;
9802                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9803                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
9804                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
9805         }
9806 }
9807 #endif
9808
9809 /*!
9810  *\brief        Guess originating forward account from MsgInfo and several 
9811  *              "common preference" settings. Return NULL if no guess. 
9812  */
9813 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
9814 {
9815         PrefsAccount *account = NULL;
9816         
9817         g_return_val_if_fail(msginfo, NULL);
9818         g_return_val_if_fail(msginfo->folder, NULL);
9819         g_return_val_if_fail(msginfo->folder->prefs, NULL);
9820
9821         if (msginfo->folder->prefs->enable_default_account)
9822                 account = account_find_from_id(msginfo->folder->prefs->default_account);
9823                 
9824         if (!account) 
9825                 account = msginfo->folder->folder->account;
9826                 
9827         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
9828                 gchar *to;
9829                 Xstrdup_a(to, msginfo->to, return NULL);
9830                 extract_address(to);
9831                 account = account_find_from_address(to);
9832         }
9833
9834         if (!account && prefs_common.forward_account_autosel) {
9835                 gchar cc[BUFFSIZE];
9836                 if (!procheader_get_header_from_msginfo
9837                         (msginfo, cc,sizeof cc , "Cc:")) { 
9838                         gchar *buf = cc + strlen("Cc:");
9839                         extract_address(buf);
9840                         account = account_find_from_address(buf);
9841                 }
9842         }
9843         
9844         if (!account && prefs_common.forward_account_autosel) {
9845                 gchar deliveredto[BUFFSIZE];
9846                 if (!procheader_get_header_from_msginfo
9847                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
9848                         gchar *buf = deliveredto + strlen("Delivered-To:");
9849                         extract_address(buf);
9850                         account = account_find_from_address(buf);
9851                 }
9852         }
9853         
9854         return account;
9855 }
9856
9857 gboolean compose_close(Compose *compose)
9858 {
9859         gint x, y;
9860
9861         if (!g_mutex_trylock(compose->mutex)) {
9862                 /* we have to wait for the (possibly deferred by auto-save)
9863                  * drafting to be done, before destroying the compose under
9864                  * it. */
9865                 debug_print("waiting for drafting to finish...\n");
9866                 compose_allow_user_actions(compose, FALSE);
9867                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
9868                 return FALSE;
9869         }
9870         g_return_val_if_fail(compose, FALSE);
9871         gtkut_widget_get_uposition(compose->window, &x, &y);
9872         prefs_common.compose_x = x;
9873         prefs_common.compose_y = y;
9874         g_mutex_unlock(compose->mutex);
9875         compose_destroy(compose);
9876         return FALSE;
9877 }
9878
9879 /**
9880  * Add entry field for each address in list.
9881  * \param compose     E-Mail composition object.
9882  * \param listAddress List of (formatted) E-Mail addresses.
9883  */
9884 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
9885         GList *node;
9886         gchar *addr;
9887         node = listAddress;
9888         while( node ) {
9889                 addr = ( gchar * ) node->data;
9890                 compose_entry_append( compose, addr, COMPOSE_TO );
9891                 node = g_list_next( node );
9892         }
9893 }
9894
9895 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
9896                                     guint action, gboolean opening_multiple)
9897 {
9898         gchar *body = NULL;
9899         GSList *new_msglist = NULL;
9900         MsgInfo *tmp_msginfo = NULL;
9901         gboolean originally_enc = FALSE;
9902         Compose *compose = NULL;
9903
9904         g_return_if_fail(msgview != NULL);
9905
9906         g_return_if_fail(msginfo_list != NULL);
9907
9908         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
9909                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
9910                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
9911
9912                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
9913                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
9914                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
9915                                                 orig_msginfo, mimeinfo);
9916                         if (tmp_msginfo != NULL) {
9917                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
9918
9919                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
9920                                 tmp_msginfo->folder = orig_msginfo->folder;
9921                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
9922                                 if (orig_msginfo->tags)
9923                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
9924                         }
9925                 }
9926         }
9927
9928         if (!opening_multiple)
9929                 body = messageview_get_selection(msgview);
9930
9931         if (new_msglist) {
9932                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
9933                 procmsg_msginfo_free(tmp_msginfo);
9934                 g_slist_free(new_msglist);
9935         } else
9936                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
9937
9938         if (originally_enc) {
9939                 compose_force_encryption(compose, compose->account, FALSE);
9940         }
9941
9942         g_free(body);
9943 }
9944
9945 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
9946                                     guint action)
9947 {
9948         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
9949         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
9950                 GSList *cur = msginfo_list;
9951                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
9952                                                "messages. Opening the windows "
9953                                                "could take some time. Do you "
9954                                                "want to continue?"), 
9955                                                g_slist_length(msginfo_list));
9956                 if (g_slist_length(msginfo_list) > 9
9957                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
9958                     != G_ALERTALTERNATE) {
9959                         g_free(msg);
9960                         return;
9961                 }
9962                 g_free(msg);
9963                 /* We'll open multiple compose windows */
9964                 /* let the WM place the next windows */
9965                 compose_force_window_origin = FALSE;
9966                 for (; cur; cur = cur->next) {
9967                         GSList tmplist;
9968                         tmplist.data = cur->data;
9969                         tmplist.next = NULL;
9970                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
9971                 }
9972                 compose_force_window_origin = TRUE;
9973         } else {
9974                 /* forwarding multiple mails as attachments is done via a
9975                  * single compose window */
9976                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
9977         }
9978 }
9979
9980 void compose_set_position(Compose *compose, gint pos)
9981 {
9982         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9983
9984         gtkut_text_view_set_position(text, pos);
9985 }
9986
9987 gboolean compose_search_string(Compose *compose,
9988                                 const gchar *str, gboolean case_sens)
9989 {
9990         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9991
9992         return gtkut_text_view_search_string(text, str, case_sens);
9993 }
9994
9995 gboolean compose_search_string_backward(Compose *compose,
9996                                 const gchar *str, gboolean case_sens)
9997 {
9998         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9999
10000         return gtkut_text_view_search_string_backward(text, str, case_sens);
10001 }
10002
10003 /* allocate a msginfo structure and populate its data from a compose data structure */
10004 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10005 {
10006         MsgInfo *newmsginfo;
10007         GSList *list;
10008         gchar buf[BUFFSIZE];
10009
10010         g_return_val_if_fail( compose != NULL, NULL );
10011
10012         newmsginfo = procmsg_msginfo_new();
10013
10014         /* date is now */
10015         get_rfc822_date(buf, sizeof(buf));
10016         newmsginfo->date = g_strdup(buf);
10017
10018         /* from */
10019         if (compose->from_name) {
10020                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10021                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10022         }
10023
10024         /* subject */
10025         if (compose->subject_entry)
10026                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10027
10028         /* to, cc, reply-to, newsgroups */
10029         for (list = compose->header_list; list; list = list->next) {
10030                 gchar *header = gtk_editable_get_chars(
10031                                                                 GTK_EDITABLE(
10032                                                                 GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
10033                 gchar *entry = gtk_editable_get_chars(
10034                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10035
10036                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10037                         if ( newmsginfo->to == NULL ) {
10038                                 newmsginfo->to = g_strdup(entry);
10039                         } else if (entry && *entry) {
10040                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10041                                 g_free(newmsginfo->to);
10042                                 newmsginfo->to = tmp;
10043                         }
10044                 } else
10045                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10046                         if ( newmsginfo->cc == NULL ) {
10047                                 newmsginfo->cc = g_strdup(entry);
10048                         } else if (entry && *entry) {
10049                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10050                                 g_free(newmsginfo->cc);
10051                                 newmsginfo->cc = tmp;
10052                         }
10053                 } else
10054                 if ( strcasecmp(header,
10055                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10056                         if ( newmsginfo->newsgroups == NULL ) {
10057                                 newmsginfo->newsgroups = g_strdup(entry);
10058                         } else if (entry && *entry) {
10059                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10060                                 g_free(newmsginfo->newsgroups);
10061                                 newmsginfo->newsgroups = tmp;
10062                         }
10063                 }
10064
10065                 g_free(header);
10066                 g_free(entry);  
10067         }
10068
10069         /* other data is unset */
10070
10071         return newmsginfo;
10072 }
10073
10074 #ifdef USE_ASPELL
10075 /* update compose's dictionaries from folder dict settings */
10076 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10077                                                 FolderItem *folder_item)
10078 {
10079         g_return_if_fail(compose != NULL);
10080
10081         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10082                 FolderItemPrefs *prefs = folder_item->prefs;
10083
10084                 if (prefs->enable_default_dictionary)
10085                         gtkaspell_change_dict(compose->gtkaspell,
10086                                         prefs->default_dictionary, FALSE);
10087                 if (folder_item->prefs->enable_default_alt_dictionary)
10088                         gtkaspell_change_alt_dict(compose->gtkaspell,
10089                                         prefs->default_alt_dictionary);
10090                 if (prefs->enable_default_dictionary
10091                         || prefs->enable_default_alt_dictionary)
10092                         compose_spell_menu_changed(compose);
10093         }
10094 }
10095 #endif
10096
10097 /*
10098  * End of Source.
10099  */