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