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