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