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