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