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