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