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