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