Add missing copyrights in tnef_parse plugin
[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->file,
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 (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         struct stat file_stat;
3579         int ret;
3580         GString *file_contents = NULL;
3581
3582         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3583
3584         /* get the size of the file we are about to insert */
3585         ret = g_stat(file, &file_stat);
3586         if (ret != 0) {
3587                 gchar *shortfile = g_path_get_basename(file);
3588                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3589                 g_free(shortfile);
3590                 return COMPOSE_INSERT_NO_FILE;
3591         } else if (prefs_common.warn_large_insert == TRUE) {
3592
3593                 /* ask user for confirmation if the file is large */
3594                 if (prefs_common.warn_large_insert_size < 0 ||
3595                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3596                         AlertValue aval;
3597                         gchar *msg;
3598
3599                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3600                                                 "in the message body. Are you sure you want to do that?"),
3601                                                 to_human_readable(file_stat.st_size));
3602                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3603                                         _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3604                         g_free(msg);
3605
3606                         /* do we ask for confirmation next time? */
3607                         if (aval & G_ALERTDISABLE) {
3608                                 /* no confirmation next time, disable feature in preferences */
3609                                 aval &= ~G_ALERTDISABLE;
3610                                 prefs_common.warn_large_insert = FALSE;
3611                         }
3612
3613                         /* abort file insertion if user canceled action */
3614                         if (aval != G_ALERTALTERNATE) {
3615                                 return COMPOSE_INSERT_NO_FILE;
3616                         }
3617                 }
3618         }
3619
3620
3621         if ((fp = g_fopen(file, "rb")) == NULL) {
3622                 FILE_OP_ERROR(file, "fopen");
3623                 return COMPOSE_INSERT_READ_ERROR;
3624         }
3625
3626         prev_autowrap = compose->autowrap;
3627         compose->autowrap = FALSE;
3628
3629         text = GTK_TEXT_VIEW(compose->text);
3630         buffer = gtk_text_view_get_buffer(text);
3631         mark = gtk_text_buffer_get_insert(buffer);
3632         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3633
3634         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3635                                         G_CALLBACK(text_inserted),
3636                                         compose);
3637
3638         cur_encoding = conv_get_locale_charset_str_no_utf8();
3639
3640         file_contents = g_string_new("");
3641         while (fgets(buf, sizeof(buf), fp) != NULL) {
3642                 gchar *str;
3643
3644                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3645                         str = g_strdup(buf);
3646                 else
3647                         str = conv_codeset_strdup
3648                                 (buf, cur_encoding, CS_INTERNAL);
3649                 if (!str) continue;
3650
3651                 /* strip <CR> if DOS/Windows file,
3652                    replace <CR> with <LF> if Macintosh file. */
3653                 strcrchomp(str);
3654                 len = strlen(str);
3655                 if (len > 0 && str[len - 1] != '\n') {
3656                         while (--len >= 0)
3657                                 if (str[len] == '\r') str[len] = '\n';
3658                 }
3659
3660                 file_contents = g_string_append(file_contents, str);
3661                 g_free(str);
3662         }
3663
3664         gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3665         g_string_free(file_contents, TRUE);
3666
3667         compose_changed_cb(NULL, compose);
3668         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3669                                           G_CALLBACK(text_inserted),
3670                                           compose);
3671         compose->autowrap = prev_autowrap;
3672         if (compose->autowrap)
3673                 compose_wrap_all(compose);
3674
3675         fclose(fp);
3676
3677         return COMPOSE_INSERT_SUCCESS;
3678 }
3679
3680 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3681                                   const gchar *filename,
3682                                   const gchar *content_type,
3683                                   const gchar *charset)
3684 {
3685         AttachInfo *ainfo;
3686         GtkTreeIter iter;
3687         FILE *fp;
3688         off_t size;
3689         GAuto *auto_ainfo;
3690         gchar *size_text;
3691         GtkListStore *store;
3692         gchar *name;
3693         gboolean has_binary = FALSE;
3694
3695         if (!is_file_exist(file)) {
3696                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3697                 gboolean result = FALSE;
3698                 if (file_from_uri && is_file_exist(file_from_uri)) {
3699                         result = compose_attach_append(
3700                                                 compose, file_from_uri,
3701                                                 filename, content_type,
3702                                                 charset);
3703                 }
3704                 g_free(file_from_uri);
3705                 if (result)
3706                         return TRUE;
3707                 alertpanel_error("File %s doesn't exist\n", filename);
3708                 return FALSE;
3709         }
3710         if ((size = get_file_size(file)) < 0) {
3711                 alertpanel_error("Can't get file size of %s\n", filename);
3712                 return FALSE;
3713         }
3714
3715         /* In batch mode, we allow 0-length files to be attached no questions asked */
3716         if (size == 0 && !compose->batch) {
3717                 gchar * msg = g_strdup_printf(_("File %s is empty."), filename);
3718                 AlertValue aval = alertpanel_full(_("Empty file"), msg, 
3719                                 GTK_STOCK_CANCEL, _("+_Attach anyway"), NULL, FALSE,
3720                                 NULL, ALERT_WARNING, G_ALERTDEFAULT);
3721                 g_free(msg);
3722
3723                 if (aval != G_ALERTALTERNATE) {
3724                         return FALSE;
3725                 }
3726         }
3727         if ((fp = g_fopen(file, "rb")) == NULL) {
3728                 alertpanel_error(_("Can't read %s."), filename);
3729                 return FALSE;
3730         }
3731         fclose(fp);
3732
3733         ainfo = g_new0(AttachInfo, 1);
3734         auto_ainfo = g_auto_pointer_new_with_free
3735                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3736         ainfo->file = g_strdup(file);
3737
3738         if (content_type) {
3739                 ainfo->content_type = g_strdup(content_type);
3740                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3741                         MsgInfo *msginfo;
3742                         MsgFlags flags = {0, 0};
3743
3744                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3745                                 ainfo->encoding = ENC_7BIT;
3746                         else
3747                                 ainfo->encoding = ENC_8BIT;
3748
3749                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3750                         if (msginfo && msginfo->subject)
3751                                 name = g_strdup(msginfo->subject);
3752                         else
3753                                 name = g_path_get_basename(filename ? filename : file);
3754
3755                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3756
3757                         procmsg_msginfo_free(msginfo);
3758                 } else {
3759                         if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3760                                 ainfo->charset = g_strdup(charset);
3761                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3762                         } else {
3763                                 ainfo->encoding = ENC_BASE64;
3764                         }
3765                         name = g_path_get_basename(filename ? filename : file);
3766                         ainfo->name = g_strdup(name);
3767                 }
3768                 g_free(name);
3769         } else {
3770                 ainfo->content_type = procmime_get_mime_type(file);
3771                 if (!ainfo->content_type) {
3772                         ainfo->content_type =
3773                                 g_strdup("application/octet-stream");
3774                         ainfo->encoding = ENC_BASE64;
3775                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3776                         ainfo->encoding =
3777                                 procmime_get_encoding_for_text_file(file, &has_binary);
3778                 else
3779                         ainfo->encoding = ENC_BASE64;
3780                 name = g_path_get_basename(filename ? filename : file);
3781                 ainfo->name = g_strdup(name);   
3782                 g_free(name);
3783         }
3784
3785         if (ainfo->name != NULL
3786         &&  !strcmp(ainfo->name, ".")) {
3787                 g_free(ainfo->name);
3788                 ainfo->name = NULL;
3789         }
3790
3791         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3792                 g_free(ainfo->content_type);
3793                 ainfo->content_type = g_strdup("application/octet-stream");
3794                 g_free(ainfo->charset);
3795                 ainfo->charset = NULL;
3796         }
3797
3798         ainfo->size = (goffset)size;
3799         size_text = to_human_readable((goffset)size);
3800
3801         store = GTK_LIST_STORE(gtk_tree_view_get_model
3802                         (GTK_TREE_VIEW(compose->attach_clist)));
3803                 
3804         gtk_list_store_append(store, &iter);
3805         gtk_list_store_set(store, &iter, 
3806                            COL_MIMETYPE, ainfo->content_type,
3807                            COL_SIZE, size_text,
3808                            COL_NAME, ainfo->name,
3809                            COL_CHARSET, ainfo->charset,
3810                            COL_DATA, ainfo,
3811                            COL_AUTODATA, auto_ainfo,
3812                            -1);
3813         
3814         g_auto_pointer_free(auto_ainfo);
3815         compose_attach_update_label(compose);
3816         return TRUE;
3817 }
3818
3819 static void compose_use_signing(Compose *compose, gboolean use_signing)
3820 {
3821         compose->use_signing = use_signing;
3822         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3823 }
3824
3825 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3826 {
3827         compose->use_encryption = use_encryption;
3828         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3829 }
3830
3831 #define NEXT_PART_NOT_CHILD(info)  \
3832 {  \
3833         node = info->node;  \
3834         while (node->children)  \
3835                 node = g_node_last_child(node);  \
3836         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3837 }
3838
3839 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3840 {
3841         MimeInfo *mimeinfo;
3842         MimeInfo *child;
3843         MimeInfo *firsttext = NULL;
3844         MimeInfo *encrypted = NULL;
3845         GNode    *node;
3846         gchar *outfile;
3847         const gchar *partname = NULL;
3848
3849         mimeinfo = procmime_scan_message(msginfo);
3850         if (!mimeinfo) return;
3851
3852         if (mimeinfo->node->children == NULL) {
3853                 procmime_mimeinfo_free_all(mimeinfo);
3854                 return;
3855         }
3856
3857         /* find first content part */
3858         child = (MimeInfo *) mimeinfo->node->children->data;
3859         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3860                 child = (MimeInfo *)child->node->children->data;
3861
3862         if (child) {
3863                 if (child->type == MIMETYPE_TEXT) {
3864                         firsttext = child;
3865                         debug_print("First text part found\n");
3866                 } else if (compose->mode == COMPOSE_REEDIT &&
3867                          child->type == MIMETYPE_APPLICATION &&
3868                          !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3869                         encrypted = (MimeInfo *)child->node->parent->data;
3870                 }
3871         }
3872         child = (MimeInfo *) mimeinfo->node->children->data;
3873         while (child != NULL) {
3874                 gint err;
3875
3876                 if (child == encrypted) {
3877                         /* skip this part of tree */
3878                         NEXT_PART_NOT_CHILD(child);
3879                         continue;
3880                 }
3881
3882                 if (child->type == MIMETYPE_MULTIPART) {
3883                         /* get the actual content */
3884                         child = procmime_mimeinfo_next(child);
3885                         continue;
3886                 }
3887                     
3888                 if (child == firsttext) {
3889                         child = procmime_mimeinfo_next(child);
3890                         continue;
3891                 }
3892
3893                 outfile = procmime_get_tmp_file_name(child);
3894                 if ((err = procmime_get_part(outfile, child)) < 0)
3895                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3896                 else {
3897                         gchar *content_type;
3898
3899                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3900
3901                         /* if we meet a pgp signature, we don't attach it, but
3902                          * we force signing. */
3903                         if ((strcmp(content_type, "application/pgp-signature") &&
3904                             strcmp(content_type, "application/pkcs7-signature") &&
3905                             strcmp(content_type, "application/x-pkcs7-signature"))
3906                             || compose->mode == COMPOSE_REDIRECT) {
3907                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3908                                 if (partname == NULL)
3909                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3910                                 if (partname == NULL)
3911                                         partname = "";
3912                                 compose_attach_append(compose, outfile, 
3913                                                       partname, content_type,
3914                                                       procmime_mimeinfo_get_parameter(child, "charset"));
3915                         } else {
3916                                 compose_force_signing(compose, compose->account, NULL);
3917                         }
3918                         g_free(content_type);
3919                 }
3920                 g_free(outfile);
3921                 NEXT_PART_NOT_CHILD(child);
3922         }
3923         procmime_mimeinfo_free_all(mimeinfo);
3924 }
3925
3926 #undef NEXT_PART_NOT_CHILD
3927
3928
3929
3930 typedef enum {
3931         WAIT_FOR_INDENT_CHAR,
3932         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3933 } IndentState;
3934
3935 /* return indent length, we allow:
3936    indent characters followed by indent characters or spaces/tabs,
3937    alphabets and numbers immediately followed by indent characters,
3938    and the repeating sequences of the above
3939    If quote ends with multiple spaces, only the first one is included. */
3940 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3941                                     const GtkTextIter *start, gint *len)
3942 {
3943         GtkTextIter iter = *start;
3944         gunichar wc;
3945         gchar ch[6];
3946         gint clen;
3947         IndentState state = WAIT_FOR_INDENT_CHAR;
3948         gboolean is_space;
3949         gboolean is_indent;
3950         gint alnum_count = 0;
3951         gint space_count = 0;
3952         gint quote_len = 0;
3953
3954         if (prefs_common.quote_chars == NULL) {
3955                 return 0 ;
3956         }
3957
3958         while (!gtk_text_iter_ends_line(&iter)) {
3959                 wc = gtk_text_iter_get_char(&iter);
3960                 if (g_unichar_iswide(wc))
3961                         break;
3962                 clen = g_unichar_to_utf8(wc, ch);
3963                 if (clen != 1)
3964                         break;
3965
3966                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3967                 is_space = g_unichar_isspace(wc);
3968
3969                 if (state == WAIT_FOR_INDENT_CHAR) {
3970                         if (!is_indent && !g_unichar_isalnum(wc))
3971                                 break;
3972                         if (is_indent) {
3973                                 quote_len += alnum_count + space_count + 1;
3974                                 alnum_count = space_count = 0;
3975                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3976                         } else
3977                                 alnum_count++;
3978                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3979                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3980                                 break;
3981                         if (is_space)
3982                                 space_count++;
3983                         else if (is_indent) {
3984                                 quote_len += alnum_count + space_count + 1;
3985                                 alnum_count = space_count = 0;
3986                         } else {
3987                                 alnum_count++;
3988                                 state = WAIT_FOR_INDENT_CHAR;
3989                         }
3990                 }
3991
3992                 gtk_text_iter_forward_char(&iter);
3993         }
3994
3995         if (quote_len > 0 && space_count > 0)
3996                 quote_len++;
3997
3998         if (len)
3999                 *len = quote_len;
4000
4001         if (quote_len > 0) {
4002                 iter = *start;
4003                 gtk_text_iter_forward_chars(&iter, quote_len);
4004                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
4005         }
4006
4007         return NULL;
4008 }
4009
4010 /* return >0 if the line is itemized */
4011 static int compose_itemized_length(GtkTextBuffer *buffer,
4012                                     const GtkTextIter *start)
4013 {
4014         GtkTextIter iter = *start;
4015         gunichar wc;
4016         gchar ch[6];
4017         gint clen;
4018         gint len = 0;
4019         if (gtk_text_iter_ends_line(&iter))
4020                 return 0;
4021
4022         while (1) {
4023                 len++;
4024                 wc = gtk_text_iter_get_char(&iter);
4025                 if (!g_unichar_isspace(wc))
4026                         break;
4027                 gtk_text_iter_forward_char(&iter);
4028                 if (gtk_text_iter_ends_line(&iter))
4029                         return 0;
4030         }
4031
4032         clen = g_unichar_to_utf8(wc, ch);
4033         if (clen != 1)
4034                 return 0;
4035
4036         if (!strchr("*-+", ch[0]))
4037                 return 0;
4038
4039         gtk_text_iter_forward_char(&iter);
4040         if (gtk_text_iter_ends_line(&iter))
4041                 return 0;
4042         wc = gtk_text_iter_get_char(&iter);
4043         if (g_unichar_isspace(wc)) {
4044                 return len+1;
4045         }
4046         return 0;
4047 }
4048
4049 /* return the string at the start of the itemization */
4050 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
4051                                     const GtkTextIter *start)
4052 {
4053         GtkTextIter iter = *start;
4054         gunichar wc;
4055         gint len = 0;
4056         GString *item_chars = g_string_new("");
4057         gchar *str = NULL;
4058
4059         if (gtk_text_iter_ends_line(&iter))
4060                 return NULL;
4061
4062         while (1) {
4063                 len++;
4064                 wc = gtk_text_iter_get_char(&iter);
4065                 if (!g_unichar_isspace(wc))
4066                         break;
4067                 gtk_text_iter_forward_char(&iter);
4068                 if (gtk_text_iter_ends_line(&iter))
4069                         break;
4070                 g_string_append_unichar(item_chars, wc);
4071         }
4072
4073         str = item_chars->str;
4074         g_string_free(item_chars, FALSE);
4075         return str;
4076 }
4077
4078 /* return the number of spaces at a line's start */
4079 static int compose_left_offset_length(GtkTextBuffer *buffer,
4080                                     const GtkTextIter *start)
4081 {
4082         GtkTextIter iter = *start;
4083         gunichar wc;
4084         gint len = 0;
4085         if (gtk_text_iter_ends_line(&iter))
4086                 return 0;
4087
4088         while (1) {
4089                 wc = gtk_text_iter_get_char(&iter);
4090                 if (!g_unichar_isspace(wc))
4091                         break;
4092                 len++;
4093                 gtk_text_iter_forward_char(&iter);
4094                 if (gtk_text_iter_ends_line(&iter))
4095                         return 0;
4096         }
4097
4098         gtk_text_iter_forward_char(&iter);
4099         if (gtk_text_iter_ends_line(&iter))
4100                 return 0;
4101         return len;
4102 }
4103
4104 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4105                                            const GtkTextIter *start,
4106                                            GtkTextIter *break_pos,
4107                                            gint max_col,
4108                                            gint quote_len)
4109 {
4110         GtkTextIter iter = *start, line_end = *start;
4111         PangoLogAttr *attrs;
4112         gchar *str;
4113         gchar *p;
4114         gint len;
4115         gint i;
4116         gint col = 0;
4117         gint pos = 0;
4118         gboolean can_break = FALSE;
4119         gboolean do_break = FALSE;
4120         gboolean was_white = FALSE;
4121         gboolean prev_dont_break = FALSE;
4122
4123         gtk_text_iter_forward_to_line_end(&line_end);
4124         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4125         len = g_utf8_strlen(str, -1);
4126         
4127         if (len == 0) {
4128                 g_free(str);
4129                 g_warning("compose_get_line_break_pos: len = 0!\n");
4130                 return FALSE;
4131         }
4132
4133         /* g_print("breaking line: %d: %s (len = %d)\n",
4134                 gtk_text_iter_get_line(&iter), str, len); */
4135
4136         attrs = g_new(PangoLogAttr, len + 1);
4137
4138         pango_default_break(str, -1, NULL, attrs, len + 1);
4139
4140         p = str;
4141
4142         /* skip quote and leading spaces */
4143         for (i = 0; *p != '\0' && i < len; i++) {
4144                 gunichar wc;
4145
4146                 wc = g_utf8_get_char(p);
4147                 if (i >= quote_len && !g_unichar_isspace(wc))
4148                         break;
4149                 if (g_unichar_iswide(wc))
4150                         col += 2;
4151                 else if (*p == '\t')
4152                         col += 8;
4153                 else
4154                         col++;
4155                 p = g_utf8_next_char(p);
4156         }
4157
4158         for (; *p != '\0' && i < len; i++) {
4159                 PangoLogAttr *attr = attrs + i;
4160                 gunichar wc;
4161                 gint uri_len;
4162
4163                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4164                         pos = i;
4165                 
4166                 was_white = attr->is_white;
4167
4168                 /* don't wrap URI */
4169                 if ((uri_len = get_uri_len(p)) > 0) {
4170                         col += uri_len;
4171                         if (pos > 0 && col > max_col) {
4172                                 do_break = TRUE;
4173                                 break;
4174                         }
4175                         i += uri_len - 1;
4176                         p += uri_len;
4177                         can_break = TRUE;
4178                         continue;
4179                 }
4180
4181                 wc = g_utf8_get_char(p);
4182                 if (g_unichar_iswide(wc)) {
4183                         col += 2;
4184                         if (prev_dont_break && can_break && attr->is_line_break)
4185                                 pos = i;
4186                 } else if (*p == '\t')
4187                         col += 8;
4188                 else
4189                         col++;
4190                 if (pos > 0 && col > max_col) {
4191                         do_break = TRUE;
4192                         break;
4193                 }
4194
4195                 if (*p == '-' || *p == '/')
4196                         prev_dont_break = TRUE;
4197                 else
4198                         prev_dont_break = FALSE;
4199
4200                 p = g_utf8_next_char(p);
4201                 can_break = TRUE;
4202         }
4203
4204 //      debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4205
4206         g_free(attrs);
4207         g_free(str);
4208
4209         *break_pos = *start;
4210         gtk_text_iter_set_line_offset(break_pos, pos);
4211
4212         return do_break;
4213 }
4214
4215 static gboolean compose_join_next_line(Compose *compose,
4216                                        GtkTextBuffer *buffer,
4217                                        GtkTextIter *iter,
4218                                        const gchar *quote_str)
4219 {
4220         GtkTextIter iter_ = *iter, cur, prev, next, end;
4221         PangoLogAttr attrs[3];
4222         gchar *str;
4223         gchar *next_quote_str;
4224         gunichar wc1, wc2;
4225         gint quote_len;
4226         gboolean keep_cursor = FALSE;
4227
4228         if (!gtk_text_iter_forward_line(&iter_) ||
4229             gtk_text_iter_ends_line(&iter_)) {
4230                 return FALSE;
4231         }
4232         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
4233
4234         if ((quote_str || next_quote_str) &&
4235             strcmp2(quote_str, next_quote_str) != 0) {
4236                 g_free(next_quote_str);
4237                 return FALSE;
4238         }
4239         g_free(next_quote_str);
4240
4241         end = iter_;
4242         if (quote_len > 0) {
4243                 gtk_text_iter_forward_chars(&end, quote_len);
4244                 if (gtk_text_iter_ends_line(&end)) {
4245                         return FALSE;
4246                 }
4247         }
4248
4249         /* don't join itemized lines */
4250         if (compose_itemized_length(buffer, &end) > 0) {
4251                 return FALSE;
4252         }
4253
4254         /* don't join signature separator */
4255         if (compose_is_sig_separator(compose, buffer, &iter_)) {
4256                 return FALSE;
4257         }
4258         /* delete quote str */
4259         if (quote_len > 0)
4260                 gtk_text_buffer_delete(buffer, &iter_, &end);
4261
4262         /* don't join line breaks put by the user */
4263         prev = cur = iter_;
4264         gtk_text_iter_backward_char(&cur);
4265         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4266                 gtk_text_iter_forward_char(&cur);
4267                 *iter = cur;
4268                 return FALSE;
4269         }
4270         gtk_text_iter_forward_char(&cur);
4271         /* delete linebreak and extra spaces */
4272         while (gtk_text_iter_backward_char(&cur)) {
4273                 wc1 = gtk_text_iter_get_char(&cur);
4274                 if (!g_unichar_isspace(wc1))
4275                         break;
4276                 prev = cur;
4277         }
4278         next = cur = iter_;
4279         while (!gtk_text_iter_ends_line(&cur)) {
4280                 wc1 = gtk_text_iter_get_char(&cur);
4281                 if (!g_unichar_isspace(wc1))
4282                         break;
4283                 gtk_text_iter_forward_char(&cur);
4284                 next = cur;
4285         }
4286         if (!gtk_text_iter_equal(&prev, &next)) {
4287                 GtkTextMark *mark;
4288
4289                 mark = gtk_text_buffer_get_insert(buffer);
4290                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4291                 if (gtk_text_iter_equal(&prev, &cur))
4292                         keep_cursor = TRUE;
4293                 gtk_text_buffer_delete(buffer, &prev, &next);
4294         }
4295         iter_ = prev;
4296
4297         /* insert space if required */
4298         gtk_text_iter_backward_char(&prev);
4299         wc1 = gtk_text_iter_get_char(&prev);
4300         wc2 = gtk_text_iter_get_char(&next);
4301         gtk_text_iter_forward_char(&next);
4302         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4303         pango_default_break(str, -1, NULL, attrs, 3);
4304         if (!attrs[1].is_line_break ||
4305             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4306                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4307                 if (keep_cursor) {
4308                         gtk_text_iter_backward_char(&iter_);
4309                         gtk_text_buffer_place_cursor(buffer, &iter_);
4310                 }
4311         }
4312         g_free(str);
4313
4314         *iter = iter_;
4315         return TRUE;
4316 }
4317
4318 #define ADD_TXT_POS(bp_, ep_, pti_) \
4319         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4320                 last = last->next; \
4321                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4322                 last->next = NULL; \
4323         } else { \
4324                 g_warning("alloc error scanning URIs\n"); \
4325         }
4326
4327 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4328 {
4329         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4330         GtkTextBuffer *buffer;
4331         GtkTextIter iter, break_pos, end_of_line;
4332         gchar *quote_str = NULL;
4333         gint quote_len;
4334         gboolean wrap_quote = force || prefs_common.linewrap_quote;
4335         gboolean prev_autowrap = compose->autowrap;
4336         gint startq_offset = -1, noq_offset = -1;
4337         gint uri_start = -1, uri_stop = -1;
4338         gint nouri_start = -1, nouri_stop = -1;
4339         gint num_blocks = 0;
4340         gint quotelevel = -1;
4341         gboolean modified = force;
4342         gboolean removed = FALSE;
4343         gboolean modified_before_remove = FALSE;
4344         gint lines = 0;
4345         gboolean start = TRUE;
4346         gint itemized_len = 0, rem_item_len = 0;
4347         gchar *itemized_chars = NULL;
4348         gboolean item_continuation = FALSE;
4349
4350         if (force) {
4351                 modified = TRUE;
4352         }
4353         if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
4354                 modified = TRUE;
4355         }
4356
4357         compose->autowrap = FALSE;
4358
4359         buffer = gtk_text_view_get_buffer(text);
4360         undo_wrapping(compose->undostruct, TRUE);
4361         if (par_iter) {
4362                 iter = *par_iter;
4363         } else {
4364                 GtkTextMark *mark;
4365                 mark = gtk_text_buffer_get_insert(buffer);
4366                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4367         }
4368
4369
4370         if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
4371                 if (gtk_text_iter_ends_line(&iter)) {
4372                         while (gtk_text_iter_ends_line(&iter) &&
4373                                gtk_text_iter_forward_line(&iter))
4374                                 ;
4375                 } else {
4376                         while (gtk_text_iter_backward_line(&iter)) {
4377                                 if (gtk_text_iter_ends_line(&iter)) {
4378                                         gtk_text_iter_forward_line(&iter);
4379                                         break;
4380                                 }
4381                         }
4382                 }
4383         } else {
4384                 /* move to line start */
4385                 gtk_text_iter_set_line_offset(&iter, 0);
4386         }
4387         
4388         itemized_len = compose_itemized_length(buffer, &iter);
4389         
4390         if (!itemized_len) {
4391                 itemized_len = compose_left_offset_length(buffer, &iter);
4392                 item_continuation = TRUE;
4393         }
4394
4395         if (itemized_len)
4396                 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4397
4398         /* go until paragraph end (empty line) */
4399         while (start || !gtk_text_iter_ends_line(&iter)) {
4400                 gchar *scanpos = NULL;
4401                 /* parse table - in order of priority */
4402                 struct table {
4403                         const gchar *needle; /* token */
4404
4405                         /* token search function */
4406                         gchar    *(*search)     (const gchar *haystack,
4407                                                  const gchar *needle);
4408                         /* part parsing function */
4409                         gboolean  (*parse)      (const gchar *start,
4410                                                  const gchar *scanpos,
4411                                                  const gchar **bp_,
4412                                                  const gchar **ep_,
4413                                                  gboolean hdr);
4414                         /* part to URI function */
4415                         gchar    *(*build_uri)  (const gchar *bp,
4416                                                  const gchar *ep);
4417                 };
4418
4419                 static struct table parser[] = {
4420                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
4421                         {"https://", strcasestr, get_uri_part,   make_uri_string},
4422                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
4423                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
4424                         {"gopher://",strcasestr, get_uri_part,   make_uri_string},
4425                         {"www.",     strcasestr, get_uri_part,   make_http_string},
4426                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
4427                         {"@",        strcasestr, get_email_part, make_email_string}
4428                 };
4429                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4430                 gint last_index = PARSE_ELEMS;
4431                 gint  n;
4432                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4433                 gint walk_pos;
4434                 
4435                 start = FALSE;
4436                 if (!prev_autowrap && num_blocks == 0) {
4437                         num_blocks++;
4438                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
4439                                         G_CALLBACK(text_inserted),
4440                                         compose);
4441                 }
4442                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4443                         goto colorize;
4444
4445                 uri_start = uri_stop = -1;
4446                 quote_len = 0;
4447                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
4448
4449                 if (quote_str) {
4450 //                      debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4451                         if (startq_offset == -1) 
4452                                 startq_offset = gtk_text_iter_get_offset(&iter);
4453                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4454                         if (quotelevel > 2) {
4455                                 /* recycle colors */
4456                                 if (prefs_common.recycle_quote_colors)
4457                                         quotelevel %= 3;
4458                                 else
4459                                         quotelevel = 2;
4460                         }
4461                         if (!wrap_quote) {
4462                                 goto colorize;
4463                         }
4464                 } else {
4465                         if (startq_offset == -1)
4466                                 noq_offset = gtk_text_iter_get_offset(&iter);
4467                         quotelevel = -1;
4468                 }
4469
4470                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4471                         goto colorize;
4472                 }
4473                 if (gtk_text_iter_ends_line(&iter)) {
4474                         goto colorize;
4475                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4476                                                prefs_common.linewrap_len,
4477                                                quote_len)) {
4478                         GtkTextIter prev, next, cur;
4479                         if (prev_autowrap != FALSE || force) {
4480                                 compose->automatic_break = TRUE;
4481                                 modified = TRUE;
4482                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4483                                 compose->automatic_break = FALSE;
4484                                 if (itemized_len && compose->autoindent) {
4485                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4486                                         if (!item_continuation)
4487                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4488                                 }
4489                         } else if (quote_str && wrap_quote) {
4490                                 compose->automatic_break = TRUE;
4491                                 modified = TRUE;
4492                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4493                                 compose->automatic_break = FALSE;
4494                                 if (itemized_len && compose->autoindent) {
4495                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4496                                         if (!item_continuation)
4497                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4498                                 }
4499                         } else 
4500                                 goto colorize;
4501                         /* remove trailing spaces */
4502                         cur = break_pos;
4503                         rem_item_len = itemized_len;
4504                         while (compose->autoindent && rem_item_len-- > 0)
4505                                 gtk_text_iter_backward_char(&cur);
4506                         gtk_text_iter_backward_char(&cur);
4507
4508                         prev = next = cur;
4509                         while (!gtk_text_iter_starts_line(&cur)) {
4510                                 gunichar wc;
4511
4512                                 gtk_text_iter_backward_char(&cur);
4513                                 wc = gtk_text_iter_get_char(&cur);
4514                                 if (!g_unichar_isspace(wc))
4515                                         break;
4516                                 prev = cur;
4517                         }
4518                         if (!gtk_text_iter_equal(&prev, &next)) {
4519                                 gtk_text_buffer_delete(buffer, &prev, &next);
4520                                 break_pos = next;
4521                                 gtk_text_iter_forward_char(&break_pos);
4522                         }
4523
4524                         if (quote_str)
4525                                 gtk_text_buffer_insert(buffer, &break_pos,
4526                                                        quote_str, -1);
4527
4528                         iter = break_pos;
4529                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4530
4531                         /* move iter to current line start */
4532                         gtk_text_iter_set_line_offset(&iter, 0);
4533                         if (quote_str) {
4534                                 g_free(quote_str);
4535                                 quote_str = NULL;
4536                         }
4537                         continue;       
4538                 } else {
4539                         /* move iter to next line start */
4540                         iter = break_pos;
4541                         lines++;
4542                 }
4543
4544 colorize:
4545                 if (!prev_autowrap && num_blocks > 0) {
4546                         num_blocks--;
4547                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4548                                         G_CALLBACK(text_inserted),
4549                                         compose);
4550                 }
4551                 end_of_line = iter;
4552                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4553                         gtk_text_iter_forward_char(&end_of_line);
4554                 }
4555                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4556
4557                 nouri_start = gtk_text_iter_get_offset(&iter);
4558                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4559
4560                 walk_pos = gtk_text_iter_get_offset(&iter);
4561                 /* FIXME: this looks phony. scanning for anything in the parse table */
4562                 for (n = 0; n < PARSE_ELEMS; n++) {
4563                         gchar *tmp;
4564
4565                         tmp = parser[n].search(walk, parser[n].needle);
4566                         if (tmp) {
4567                                 if (scanpos == NULL || tmp < scanpos) {
4568                                         scanpos = tmp;
4569                                         last_index = n;
4570                                 }
4571                         }                                       
4572                 }
4573
4574                 bp = ep = 0;
4575                 if (scanpos) {
4576                         /* check if URI can be parsed */
4577                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4578                                         (const gchar **)&ep, FALSE)
4579                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4580                                         walk = ep;
4581                         } else
4582                                 walk = scanpos +
4583                                         strlen(parser[last_index].needle);
4584                 } 
4585                 if (bp && ep) {
4586                         uri_start = walk_pos + (bp - o_walk);
4587                         uri_stop  = walk_pos + (ep - o_walk);
4588                 }
4589                 g_free(o_walk);
4590                 o_walk = NULL;
4591                 gtk_text_iter_forward_line(&iter);
4592                 g_free(quote_str);
4593                 quote_str = NULL;
4594                 if (startq_offset != -1) {
4595                         GtkTextIter startquote, endquote;
4596                         gtk_text_buffer_get_iter_at_offset(
4597                                 buffer, &startquote, startq_offset);
4598                         endquote = iter;
4599
4600                         switch (quotelevel) {
4601                         case 0: 
4602                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4603                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4604                                         gtk_text_buffer_apply_tag_by_name(
4605                                                 buffer, "quote0", &startquote, &endquote);
4606                                         gtk_text_buffer_remove_tag_by_name(
4607                                                 buffer, "quote1", &startquote, &endquote);
4608                                         gtk_text_buffer_remove_tag_by_name(
4609                                                 buffer, "quote2", &startquote, &endquote);
4610                                         modified = TRUE;
4611                                 }
4612                                 break;
4613                         case 1: 
4614                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4615                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4616                                         gtk_text_buffer_apply_tag_by_name(
4617                                                 buffer, "quote1", &startquote, &endquote);
4618                                         gtk_text_buffer_remove_tag_by_name(
4619                                                 buffer, "quote0", &startquote, &endquote);
4620                                         gtk_text_buffer_remove_tag_by_name(
4621                                                 buffer, "quote2", &startquote, &endquote);
4622                                         modified = TRUE;
4623                                 }
4624                                 break;
4625                         case 2: 
4626                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4627                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4628                                         gtk_text_buffer_apply_tag_by_name(
4629                                                 buffer, "quote2", &startquote, &endquote);
4630                                         gtk_text_buffer_remove_tag_by_name(
4631                                                 buffer, "quote0", &startquote, &endquote);
4632                                         gtk_text_buffer_remove_tag_by_name(
4633                                                 buffer, "quote1", &startquote, &endquote);
4634                                         modified = TRUE;
4635                                 }
4636                                 break;
4637                         }
4638                         startq_offset = -1;
4639                 } else if (noq_offset != -1) {
4640                         GtkTextIter startnoquote, endnoquote;
4641                         gtk_text_buffer_get_iter_at_offset(
4642                                 buffer, &startnoquote, noq_offset);
4643                         endnoquote = iter;
4644
4645                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4646                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4647                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4648                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4649                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4650                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4651                                 gtk_text_buffer_remove_tag_by_name(
4652                                         buffer, "quote0", &startnoquote, &endnoquote);
4653                                 gtk_text_buffer_remove_tag_by_name(
4654                                         buffer, "quote1", &startnoquote, &endnoquote);
4655                                 gtk_text_buffer_remove_tag_by_name(
4656                                         buffer, "quote2", &startnoquote, &endnoquote);
4657                                 modified = TRUE;
4658                         }
4659                         noq_offset = -1;
4660                 }
4661                 
4662                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4663                         GtkTextIter nouri_start_iter, nouri_end_iter;
4664                         gtk_text_buffer_get_iter_at_offset(
4665                                 buffer, &nouri_start_iter, nouri_start);
4666                         gtk_text_buffer_get_iter_at_offset(
4667                                 buffer, &nouri_end_iter, nouri_stop);
4668                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4669                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4670                                 gtk_text_buffer_remove_tag_by_name(
4671                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4672                                 modified_before_remove = modified;
4673                                 modified = TRUE;
4674                                 removed = TRUE;
4675                         }
4676                 }
4677                 if (uri_start >= 0 && uri_stop > 0) {
4678                         GtkTextIter uri_start_iter, uri_end_iter, back;
4679                         gtk_text_buffer_get_iter_at_offset(
4680                                 buffer, &uri_start_iter, uri_start);
4681                         gtk_text_buffer_get_iter_at_offset(
4682                                 buffer, &uri_end_iter, uri_stop);
4683                         back = uri_end_iter;
4684                         gtk_text_iter_backward_char(&back);
4685                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4686                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4687                                 gtk_text_buffer_apply_tag_by_name(
4688                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4689                                 modified = TRUE;
4690                                 if (removed && !modified_before_remove) {
4691                                         modified = FALSE;
4692                                 } 
4693                         }
4694                 }
4695                 if (!modified) {
4696 //                      debug_print("not modified, out after %d lines\n", lines);
4697                         goto end;
4698                 }
4699         }
4700 //      debug_print("modified, out after %d lines\n", lines);
4701 end:
4702         g_free(itemized_chars);
4703         if (par_iter)
4704                 *par_iter = iter;
4705         undo_wrapping(compose->undostruct, FALSE);
4706         compose->autowrap = prev_autowrap;
4707
4708         return modified;
4709 }
4710
4711 void compose_action_cb(void *data)
4712 {
4713         Compose *compose = (Compose *)data;
4714         compose_wrap_all(compose);
4715 }
4716
4717 static void compose_wrap_all(Compose *compose)
4718 {
4719         compose_wrap_all_full(compose, FALSE);
4720 }
4721
4722 static void compose_wrap_all_full(Compose *compose, gboolean force)
4723 {
4724         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4725         GtkTextBuffer *buffer;
4726         GtkTextIter iter;
4727         gboolean modified = TRUE;
4728
4729         buffer = gtk_text_view_get_buffer(text);
4730
4731         gtk_text_buffer_get_start_iter(buffer, &iter);
4732
4733         undo_wrapping(compose->undostruct, TRUE);
4734
4735         while (!gtk_text_iter_is_end(&iter) && modified)
4736                 modified = compose_beautify_paragraph(compose, &iter, force);
4737
4738         undo_wrapping(compose->undostruct, FALSE);
4739
4740 }
4741
4742 static void compose_set_title(Compose *compose)
4743 {
4744         gchar *str;
4745         gchar *edited;
4746         gchar *subject;
4747         
4748         edited = compose->modified ? _(" [Edited]") : "";
4749         
4750         subject = gtk_editable_get_chars(
4751                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4752
4753 #ifndef GENERIC_UMPC
4754         if (subject && strlen(subject))
4755                 str = g_strdup_printf(_("%s - Compose message%s"),
4756                                       subject, edited); 
4757         else
4758                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4759 #else
4760         str = g_strdup(_("Compose message"));
4761 #endif
4762
4763         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4764         g_free(str);
4765         g_free(subject);
4766 }
4767
4768 /**
4769  * compose_current_mail_account:
4770  * 
4771  * Find a current mail account (the currently selected account, or the
4772  * default account, if a news account is currently selected).  If a
4773  * mail account cannot be found, display an error message.
4774  * 
4775  * Return value: Mail account, or NULL if not found.
4776  **/
4777 static PrefsAccount *
4778 compose_current_mail_account(void)
4779 {
4780         PrefsAccount *ac;
4781
4782         if (cur_account && cur_account->protocol != A_NNTP)
4783                 ac = cur_account;
4784         else {
4785                 ac = account_get_default();
4786                 if (!ac || ac->protocol == A_NNTP) {
4787                         alertpanel_error(_("Account for sending mail is not specified.\n"
4788                                            "Please select a mail account before sending."));
4789                         return NULL;
4790                 }
4791         }
4792         return ac;
4793 }
4794
4795 #define QUOTE_IF_REQUIRED(out, str)                                     \
4796 {                                                                       \
4797         if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) {           \
4798                 gchar *__tmp;                                           \
4799                 gint len;                                               \
4800                                                                         \
4801                 len = strlen(str) + 3;                                  \
4802                 if ((__tmp = alloca(len)) == NULL) {                    \
4803                         g_warning("can't allocate memory\n");           \
4804                         g_string_free(header, TRUE);                    \
4805                         return NULL;                                    \
4806                 }                                                       \
4807                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4808                 out = __tmp;                                            \
4809         } else {                                                        \
4810                 gchar *__tmp;                                           \
4811                                                                         \
4812                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4813                         g_warning("can't allocate memory\n");           \
4814                         g_string_free(header, TRUE);                    \
4815                         return NULL;                                    \
4816                 } else                                                  \
4817                         strcpy(__tmp, str);                             \
4818                                                                         \
4819                 out = __tmp;                                            \
4820         }                                                               \
4821 }
4822
4823 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4824 {                                                                       \
4825         if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) {           \
4826                 gchar *__tmp;                                           \
4827                 gint len;                                               \
4828                                                                         \
4829                 len = strlen(str) + 3;                                  \
4830                 if ((__tmp = alloca(len)) == NULL) {                    \
4831                         g_warning("can't allocate memory\n");           \
4832                         errret;                                         \
4833                 }                                                       \
4834                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4835                 out = __tmp;                                            \
4836         } else {                                                        \
4837                 gchar *__tmp;                                           \
4838                                                                         \
4839                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4840                         g_warning("can't allocate memory\n");           \
4841                         errret;                                         \
4842                 } else                                                  \
4843                         strcpy(__tmp, str);                             \
4844                                                                         \
4845                 out = __tmp;                                            \
4846         }                                                               \
4847 }
4848
4849 static void compose_select_account(Compose *compose, PrefsAccount *account,
4850                                    gboolean init)
4851 {
4852         gchar *from = NULL, *header = NULL;
4853         ComposeHeaderEntry *header_entry;
4854 #if GTK_CHECK_VERSION(2, 24, 0)
4855         GtkTreeIter iter;
4856 #endif
4857
4858         cm_return_if_fail(account != NULL);
4859
4860         compose->account = account;
4861         if (account->name && *account->name) {
4862                 gchar *buf, *qbuf;
4863                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4864                 qbuf = escape_internal_quotes(buf, '"');
4865                 from = g_strdup_printf("%s <%s>",
4866                                        qbuf, account->address);
4867                 if (qbuf != buf)
4868                         g_free(qbuf);
4869                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4870         } else {
4871                 from = g_strdup_printf("<%s>",
4872                                        account->address);
4873                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4874         }
4875
4876         g_free(from);
4877
4878         compose_set_title(compose);
4879
4880         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4881                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4882         else
4883                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4884         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4885                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4886         else
4887                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4888                                        
4889         activate_privacy_system(compose, account, FALSE);
4890
4891         if (!init && compose->mode != COMPOSE_REDIRECT) {
4892                 undo_block(compose->undostruct);
4893                 compose_insert_sig(compose, TRUE);
4894                 undo_unblock(compose->undostruct);
4895         }
4896         
4897         header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4898 #if !GTK_CHECK_VERSION(2, 24, 0)
4899         header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4900 #else
4901         if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry->combo), &iter))
4902                 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4903                         header_entry->combo)), &iter, COMBOBOX_TEXT, &header, -1);
4904 #endif
4905         
4906         if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4907                 if (account->protocol == A_NNTP) {
4908                         if (!strcmp(header, _("To:")))
4909                                 combobox_select_by_text(
4910                                         GTK_COMBO_BOX(header_entry->combo),
4911                                         _("Newsgroups:"));
4912                 } else {
4913                         if (!strcmp(header, _("Newsgroups:")))
4914                                 combobox_select_by_text(
4915                                         GTK_COMBO_BOX(header_entry->combo),
4916                                         _("To:"));
4917                 }
4918                 
4919         }
4920         g_free(header);
4921         
4922 #ifdef USE_ENCHANT
4923         /* use account's dict info if set */
4924         if (compose->gtkaspell) {
4925                 if (account->enable_default_dictionary)
4926                         gtkaspell_change_dict(compose->gtkaspell,
4927                                         account->default_dictionary, FALSE);
4928                 if (account->enable_default_alt_dictionary)
4929                         gtkaspell_change_alt_dict(compose->gtkaspell,
4930                                         account->default_alt_dictionary);
4931                 if (account->enable_default_dictionary
4932                         || account->enable_default_alt_dictionary)
4933                         compose_spell_menu_changed(compose);
4934         }
4935 #endif
4936 }
4937
4938 gboolean compose_check_for_valid_recipient(Compose *compose) {
4939         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4940         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4941         gboolean recipient_found = FALSE;
4942         GSList *list;
4943         gchar **strptr;
4944
4945         /* free to and newsgroup list */
4946         slist_free_strings_full(compose->to_list);
4947         compose->to_list = NULL;
4948                         
4949         slist_free_strings_full(compose->newsgroup_list);
4950         compose->newsgroup_list = NULL;
4951
4952         /* search header entries for to and newsgroup entries */
4953         for (list = compose->header_list; list; list = list->next) {
4954                 gchar *header;
4955                 gchar *entry;
4956                 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4957                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4958                 g_strstrip(entry);
4959                 g_strstrip(header);
4960                 if (entry[0] != '\0') {
4961                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4962                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4963                                         compose->to_list = address_list_append(compose->to_list, entry);
4964                                         recipient_found = TRUE;
4965                                 }
4966                         }
4967                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4968                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4969                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4970                                         recipient_found = TRUE;
4971                                 }
4972                         }
4973                 }
4974                 g_free(header);
4975                 g_free(entry);
4976         }
4977         return recipient_found;
4978 }
4979
4980 static gboolean compose_check_for_set_recipients(Compose *compose)
4981 {
4982         if (compose->account->set_autocc && compose->account->auto_cc) {
4983                 gboolean found_other = FALSE;
4984                 GSList *list;
4985                 /* search header entries for to and newsgroup entries */
4986                 for (list = compose->header_list; list; list = list->next) {
4987                         gchar *entry;
4988                         gchar *header;
4989                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4990                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4991                         g_strstrip(entry);
4992                         g_strstrip(header);
4993                         if (strcmp(entry, compose->account->auto_cc)
4994                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4995                                 found_other = TRUE;
4996                                 g_free(entry);
4997                                 break;
4998                         }
4999                         g_free(entry);
5000                         g_free(header);
5001                 }
5002                 if (!found_other) {
5003                         AlertValue aval;
5004                         if (compose->batch) {
5005                                 gtk_widget_show_all(compose->window);
5006                         }
5007                         aval = alertpanel(_("Send"),
5008                                           _("The only recipient is the default CC address. Send anyway?"),
5009                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
5010                         if (aval != G_ALERTALTERNATE)
5011                                 return FALSE;
5012                 }
5013         }
5014         if (compose->account->set_autobcc && compose->account->auto_bcc) {
5015                 gboolean found_other = FALSE;
5016                 GSList *list;
5017                 /* search header entries for to and newsgroup entries */
5018                 for (list = compose->header_list; list; list = list->next) {
5019                         gchar *entry;
5020                         gchar *header;
5021                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5022                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5023                         g_strstrip(entry);
5024                         g_strstrip(header);
5025                         if (strcmp(entry, compose->account->auto_bcc)
5026                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
5027                                 found_other = TRUE;
5028                                 g_free(entry);
5029                                 break;
5030                         }
5031                         g_free(entry);
5032                         g_free(header);
5033                 }
5034                 if (!found_other) {
5035                         AlertValue aval;
5036                         if (compose->batch) {
5037                                 gtk_widget_show_all(compose->window);
5038                         }
5039                         aval = alertpanel(_("Send"),
5040                                           _("The only recipient is the default BCC address. Send anyway?"),
5041                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
5042                         if (aval != G_ALERTALTERNATE)
5043                                 return FALSE;
5044                 }
5045         }
5046         return TRUE;
5047 }
5048
5049 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
5050 {
5051         const gchar *str;
5052
5053         if (compose_check_for_valid_recipient(compose) == FALSE) {
5054                 if (compose->batch) {
5055                         gtk_widget_show_all(compose->window);
5056                 }
5057                 alertpanel_error(_("Recipient is not specified."));
5058                 return FALSE;
5059         }
5060
5061         if (compose_check_for_set_recipients(compose) == FALSE) {
5062                 return FALSE;
5063         }
5064
5065         if (!compose->batch && prefs_common.warn_empty_subj == TRUE) {
5066                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5067                 if (*str == '\0' && check_everything == TRUE &&
5068                     compose->mode != COMPOSE_REDIRECT) {
5069                         AlertValue aval;
5070                         gchar *button_label;
5071                         gchar *message;
5072
5073                         if (compose->sending)
5074                                 button_label = _("+_Send");
5075                         else
5076                                 button_label = _("+_Queue");
5077                         message = g_strdup_printf(_("Subject is empty. %s"),
5078                                         compose->sending?_("Send it anyway?"):
5079                                         _("Queue it anyway?"));
5080
5081                         aval = alertpanel_full(compose->sending?_("Send"):_("Send later"), message,
5082                                                GTK_STOCK_CANCEL, button_label, NULL, TRUE, NULL,
5083                                                ALERT_QUESTION, G_ALERTDEFAULT);
5084                         g_free(message);
5085                         if (aval & G_ALERTDISABLE) {
5086                                 aval &= ~G_ALERTDISABLE;
5087                                 prefs_common.warn_empty_subj = FALSE;
5088                         }
5089                         if (aval != G_ALERTALTERNATE)
5090                                 return FALSE;
5091                 }
5092         }
5093
5094         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5095                 return FALSE;
5096
5097         return TRUE;
5098 }
5099
5100 gint compose_send(Compose *compose)
5101 {
5102         gint msgnum;
5103         FolderItem *folder = NULL;
5104         gint val = -1;
5105         gchar *msgpath = NULL;
5106         gboolean discard_window = FALSE;
5107         gchar *errstr = NULL;
5108         gchar *tmsgid = NULL;
5109         MainWindow *mainwin = mainwindow_get_mainwindow();
5110         gboolean queued_removed = FALSE;
5111
5112         if (prefs_common.send_dialog_invisible
5113                         || compose->batch == TRUE)
5114                 discard_window = TRUE;
5115
5116         compose_allow_user_actions (compose, FALSE);
5117         compose->sending = TRUE;
5118
5119         if (compose_check_entries(compose, TRUE) == FALSE) {
5120                 if (compose->batch) {
5121                         gtk_widget_show_all(compose->window);
5122                 }
5123                 goto bail;
5124         }
5125
5126         inc_lock();
5127         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5128
5129         if (val) {
5130                 if (compose->batch) {
5131                         gtk_widget_show_all(compose->window);
5132                 }
5133                 if (val == -4) {
5134                         alertpanel_error(_("Could not queue message for sending:\n\n"
5135                                            "Charset conversion failed."));
5136                 } else if (val == -5) {
5137                         alertpanel_error(_("Could not queue message for sending:\n\n"
5138                                            "Couldn't get recipient encryption key."));
5139                 } else if (val == -6) {
5140                         /* silent error */
5141                 } else if (val == -3) {
5142                         if (privacy_peek_error())
5143                         alertpanel_error(_("Could not queue message for sending:\n\n"
5144                                            "Signature failed: %s"), privacy_get_error());
5145                 } else if (val == -2 && errno != 0) {
5146                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5147                 } else {
5148                         alertpanel_error(_("Could not queue message for sending."));
5149                 }
5150                 goto bail;
5151         }
5152
5153         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5154         if (discard_window) {
5155                 compose->sending = FALSE;
5156                 compose_close(compose);
5157                 /* No more compose access in the normal codepath 
5158                  * after this point! */
5159                 compose = NULL;
5160         }
5161
5162         if (msgnum == 0) {
5163                 alertpanel_error(_("The message was queued but could not be "
5164                                    "sent.\nUse \"Send queued messages\" from "
5165                                    "the main window to retry."));
5166                 if (!discard_window) {
5167                         goto bail;
5168                 }
5169                 inc_unlock();
5170                 g_free(tmsgid);
5171                 return -1;
5172         }
5173         if (msgpath == NULL) {
5174                 msgpath = folder_item_fetch_msg(folder, msgnum);
5175                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5176                 g_free(msgpath);
5177         } else {
5178                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5179                 claws_unlink(msgpath);
5180                 g_free(msgpath);
5181         }
5182         if (!discard_window) {
5183                 if (val != 0) {
5184                         if (!queued_removed)
5185                                 folder_item_remove_msg(folder, msgnum);
5186                         folder_item_scan(folder);
5187                         if (tmsgid) {
5188                                 /* make sure we delete that */
5189                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5190                                 if (tmp) {
5191                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5192                                         folder_item_remove_msg(folder, tmp->msgnum);
5193                                         procmsg_msginfo_free(tmp);
5194                                 } 
5195                         }
5196                 }
5197         }
5198
5199         if (val == 0) {
5200                 if (!queued_removed)
5201                         folder_item_remove_msg(folder, msgnum);
5202                 folder_item_scan(folder);
5203                 if (tmsgid) {
5204                         /* make sure we delete that */
5205                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5206                         if (tmp) {
5207                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5208                                 folder_item_remove_msg(folder, tmp->msgnum);
5209                                 procmsg_msginfo_free(tmp);
5210                         }
5211                 }
5212                 if (!discard_window) {
5213                         compose->sending = FALSE;
5214                         compose_allow_user_actions (compose, TRUE);
5215                         compose_close(compose);
5216                 }
5217         } else {
5218                 if (errstr) {
5219                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5220                                    "the main window to retry."), errstr);
5221                         g_free(errstr);
5222                 } else {
5223                         alertpanel_error_log(_("The message was queued but could not be "
5224                                    "sent.\nUse \"Send queued messages\" from "
5225                                    "the main window to retry."));
5226                 }
5227                 if (!discard_window) {
5228                         goto bail;              
5229                 }
5230                 inc_unlock();
5231                 g_free(tmsgid);
5232                 return -1;
5233         }
5234         g_free(tmsgid);
5235         inc_unlock();
5236         toolbar_main_set_sensitive(mainwin);
5237         main_window_set_menu_sensitive(mainwin);
5238         return 0;
5239
5240 bail:
5241         inc_unlock();
5242         g_free(tmsgid);
5243         compose_allow_user_actions (compose, TRUE);
5244         compose->sending = FALSE;
5245         compose->modified = TRUE; 
5246         toolbar_main_set_sensitive(mainwin);
5247         main_window_set_menu_sensitive(mainwin);
5248
5249         return -1;
5250 }
5251
5252 static gboolean compose_use_attach(Compose *compose) 
5253 {
5254         GtkTreeModel *model = gtk_tree_view_get_model
5255                                 (GTK_TREE_VIEW(compose->attach_clist));
5256         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5257 }
5258
5259 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5260                                                            FILE *fp)
5261 {
5262         gchar buf[BUFFSIZE];
5263         gchar *str;
5264         gboolean first_to_address;
5265         gboolean first_cc_address;
5266         GSList *list;
5267         ComposeHeaderEntry *headerentry;
5268         const gchar *headerentryname;
5269         const gchar *cc_hdr;
5270         const gchar *to_hdr;
5271         gboolean err = FALSE;
5272
5273         debug_print("Writing redirect header\n");
5274
5275         cc_hdr = prefs_common_translated_header_name("Cc:");
5276         to_hdr = prefs_common_translated_header_name("To:");
5277
5278         first_to_address = TRUE;
5279         for (list = compose->header_list; list; list = list->next) {
5280                 headerentry = ((ComposeHeaderEntry *)list->data);
5281                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5282
5283                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5284                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5285                         Xstrdup_a(str, entstr, return -1);
5286                         g_strstrip(str);
5287                         if (str[0] != '\0') {
5288                                 compose_convert_header
5289                                         (compose, buf, sizeof(buf), str,
5290                                         strlen("Resent-To") + 2, TRUE);
5291
5292                                 if (first_to_address) {
5293                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5294                                         first_to_address = FALSE;
5295                                 } else {
5296                                         err |= (fprintf(fp, ",") < 0);
5297                                 }
5298                                 err |= (fprintf(fp, "%s", buf) < 0);
5299                         }
5300                 }
5301         }
5302         if (!first_to_address) {
5303                 err |= (fprintf(fp, "\n") < 0);
5304         }
5305
5306         first_cc_address = TRUE;
5307         for (list = compose->header_list; list; list = list->next) {
5308                 headerentry = ((ComposeHeaderEntry *)list->data);
5309                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5310
5311                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5312                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5313                         Xstrdup_a(str, strg, return -1);
5314                         g_strstrip(str);
5315                         if (str[0] != '\0') {
5316                                 compose_convert_header
5317                                         (compose, buf, sizeof(buf), str,
5318                                         strlen("Resent-Cc") + 2, TRUE);
5319
5320                                 if (first_cc_address) {
5321                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5322                                         first_cc_address = FALSE;
5323                                 } else {
5324                                         err |= (fprintf(fp, ",") < 0);
5325                                 }
5326                                 err |= (fprintf(fp, "%s", buf) < 0);
5327                         }
5328                 }
5329         }
5330         if (!first_cc_address) {
5331                 err |= (fprintf(fp, "\n") < 0);
5332         }
5333         
5334         return (err ? -1:0);
5335 }
5336
5337 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5338 {
5339         gchar buf[BUFFSIZE];
5340         gchar *str;
5341         const gchar *entstr;
5342         /* struct utsname utsbuf; */
5343         gboolean err = FALSE;
5344
5345         cm_return_val_if_fail(fp != NULL, -1);
5346         cm_return_val_if_fail(compose->account != NULL, -1);
5347         cm_return_val_if_fail(compose->account->address != NULL, -1);
5348
5349         /* Resent-Date */
5350         get_rfc822_date(buf, sizeof(buf));
5351         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5352
5353         /* Resent-From */
5354         if (compose->account->name && *compose->account->name) {
5355                 compose_convert_header
5356                         (compose, buf, sizeof(buf), compose->account->name,
5357                          strlen("From: "), TRUE);
5358                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5359                         buf, compose->account->address) < 0);
5360         } else
5361                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5362
5363         /* Subject */
5364         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5365         if (*entstr != '\0') {
5366                 Xstrdup_a(str, entstr, return -1);
5367                 g_strstrip(str);
5368                 if (*str != '\0') {
5369                         compose_convert_header(compose, buf, sizeof(buf), str,
5370                                                strlen("Subject: "), FALSE);
5371                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5372                 }
5373         }
5374
5375         /* Resent-Message-ID */
5376         if (compose->account->set_domain && compose->account->domain) {
5377                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5378         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5379                 g_snprintf(buf, sizeof(buf), "%s", 
5380                         strchr(compose->account->address, '@') ?
5381                                 strchr(compose->account->address, '@')+1 :
5382                                 compose->account->address);
5383         } else {
5384                 g_snprintf(buf, sizeof(buf), "%s", "");
5385         }
5386
5387         if (compose->account->gen_msgid) {
5388                 gchar *addr = NULL;
5389                 if (compose->account->msgid_with_addr) {
5390                         addr = compose->account->address;
5391                 }
5392                 generate_msgid(buf, sizeof(buf), addr);
5393                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5394                 if (compose->msgid)
5395                         g_free(compose->msgid);
5396                 compose->msgid = g_strdup(buf);
5397         } else {
5398                 compose->msgid = NULL;
5399         }
5400
5401         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5402                 return -1;
5403
5404         /* separator between header and body */
5405         err |= (fputs("\n", fp) == EOF);
5406
5407         return (err ? -1:0);
5408 }
5409
5410 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5411 {
5412         FILE *fp;
5413         size_t len;
5414         gchar buf[BUFFSIZE];
5415         int i = 0;
5416         gboolean skip = FALSE;
5417         gboolean err = FALSE;
5418         gchar *not_included[]={
5419                 "Return-Path:",         "Delivered-To:",        "Received:",
5420                 "Subject:",             "X-UIDL:",              "AF:",
5421                 "NF:",                  "PS:",                  "SRH:",
5422                 "SFN:",                 "DSR:",                 "MID:",
5423                 "CFG:",                 "PT:",                  "S:",
5424                 "RQ:",                  "SSV:",                 "NSV:",
5425                 "SSH:",                 "R:",                   "MAID:",
5426                 "NAID:",                "RMID:",                "FMID:",
5427                 "SCF:",                 "RRCPT:",               "NG:",
5428                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5429                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5430                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5431                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5432                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5433                 NULL
5434                 };
5435         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5436                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5437                 return -1;
5438         }
5439
5440         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5441                 skip = FALSE;
5442                 for (i = 0; not_included[i] != NULL; i++) {
5443                         if (g_ascii_strncasecmp(buf, not_included[i],
5444                                                 strlen(not_included[i])) == 0) {
5445                                 skip = TRUE;
5446                                 break;
5447                         }
5448                 }
5449                 if (skip)
5450                         continue;
5451                 if (fputs(buf, fdest) == -1)
5452                         goto error;
5453
5454                 if (!prefs_common.redirect_keep_from) {
5455                         if (g_ascii_strncasecmp(buf, "From:",
5456                                           strlen("From:")) == 0) {
5457                                 err |= (fputs(" (by way of ", fdest) == EOF);
5458                                 if (compose->account->name
5459                                     && *compose->account->name) {
5460                                         compose_convert_header
5461                                                 (compose, buf, sizeof(buf),
5462                                                  compose->account->name,
5463                                                  strlen("From: "),
5464                                                  FALSE);
5465                                         err |= (fprintf(fdest, "%s <%s>",
5466                                                 buf,
5467                                                 compose->account->address) < 0);
5468                                 } else
5469                                         err |= (fprintf(fdest, "%s",
5470                                                 compose->account->address) < 0);
5471                                 err |= (fputs(")", fdest) == EOF);
5472                         }
5473                 }
5474
5475                 if (fputs("\n", fdest) == -1)
5476                         goto error;
5477         }
5478
5479         if (err)
5480                 goto error;
5481
5482         if (compose_redirect_write_headers(compose, fdest))
5483                 goto error;
5484
5485         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5486                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5487                         goto error;
5488         }
5489
5490         fclose(fp);
5491
5492         return 0;
5493 error:
5494         fclose(fp);
5495
5496         return -1;
5497 }
5498
5499 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5500 {
5501         GtkTextBuffer *buffer;
5502         GtkTextIter start, end;
5503         gchar *chars;
5504         gchar *buf;
5505         const gchar *out_codeset;
5506         EncodingType encoding = ENC_UNKNOWN;
5507         MimeInfo *mimemsg, *mimetext;
5508         gint line;
5509         const gchar *src_codeset = CS_INTERNAL;
5510         gchar *from_addr = NULL;
5511         gchar *from_name = NULL;
5512
5513         if (action == COMPOSE_WRITE_FOR_SEND)
5514                 attach_parts = TRUE;
5515
5516         /* create message MimeInfo */
5517         mimemsg = procmime_mimeinfo_new();
5518         mimemsg->type = MIMETYPE_MESSAGE;
5519         mimemsg->subtype = g_strdup("rfc822");
5520         mimemsg->content = MIMECONTENT_MEM;
5521         mimemsg->tmp = TRUE; /* must free content later */
5522         mimemsg->data.mem = compose_get_header(compose);
5523
5524         /* Create text part MimeInfo */
5525         /* get all composed text */
5526         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5527         gtk_text_buffer_get_start_iter(buffer, &start);
5528         gtk_text_buffer_get_end_iter(buffer, &end);
5529         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5530
5531         out_codeset = conv_get_charset_str(compose->out_encoding);
5532
5533         if (!out_codeset && is_ascii_str(chars)) {
5534                 out_codeset = CS_US_ASCII;
5535         } else if (prefs_common.outgoing_fallback_to_ascii &&
5536                    is_ascii_str(chars)) {
5537                 out_codeset = CS_US_ASCII;
5538                 encoding = ENC_7BIT;
5539         }
5540
5541         if (!out_codeset) {
5542                 gchar *test_conv_global_out = NULL;
5543                 gchar *test_conv_reply = NULL;
5544
5545                 /* automatic mode. be automatic. */
5546                 codeconv_set_strict(TRUE);
5547
5548                 out_codeset = conv_get_outgoing_charset_str();
5549                 if (out_codeset) {
5550                         debug_print("trying to convert to %s\n", out_codeset);
5551                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5552                 }
5553
5554                 if (!test_conv_global_out && compose->orig_charset
5555                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5556                         out_codeset = compose->orig_charset;
5557                         debug_print("failure; trying to convert to %s\n", out_codeset);
5558                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5559                 }
5560
5561                 if (!test_conv_global_out && !test_conv_reply) {
5562                         /* we're lost */
5563                         out_codeset = CS_INTERNAL;
5564                         debug_print("failure; finally using %s\n", out_codeset);
5565                 }
5566                 g_free(test_conv_global_out);
5567                 g_free(test_conv_reply);
5568                 codeconv_set_strict(FALSE);
5569         }
5570
5571         if (encoding == ENC_UNKNOWN) {
5572                 if (prefs_common.encoding_method == CTE_BASE64)
5573                         encoding = ENC_BASE64;
5574                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5575                         encoding = ENC_QUOTED_PRINTABLE;
5576                 else if (prefs_common.encoding_method == CTE_8BIT)
5577                         encoding = ENC_8BIT;
5578                 else
5579                         encoding = procmime_get_encoding_for_charset(out_codeset);
5580         }
5581
5582         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5583                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5584
5585         if (action == COMPOSE_WRITE_FOR_SEND) {
5586                 codeconv_set_strict(TRUE);
5587                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5588                 codeconv_set_strict(FALSE);
5589
5590                 if (!buf) {
5591                         AlertValue aval;
5592                         gchar *msg;
5593
5594                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5595                                                 "to the specified %s charset.\n"
5596                                                 "Send it as %s?"), out_codeset, src_codeset);
5597                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5598                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5599                         g_free(msg);
5600
5601                         if (aval != G_ALERTALTERNATE) {
5602                                 g_free(chars);
5603                                 return -3;
5604                         } else {
5605                                 buf = chars;
5606                                 out_codeset = src_codeset;
5607                                 chars = NULL;
5608                         }
5609                 }
5610         } else {
5611                 buf = chars;
5612                 out_codeset = src_codeset;
5613                 chars = NULL;
5614         }
5615         g_free(chars);
5616
5617         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5618                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5619                     strstr(buf, "\nFrom ") != NULL) {
5620                         encoding = ENC_QUOTED_PRINTABLE;
5621                 }
5622         }
5623
5624         mimetext = procmime_mimeinfo_new();
5625         mimetext->content = MIMECONTENT_MEM;
5626         mimetext->tmp = TRUE; /* must free content later */
5627         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5628          * and free the data, which we need later. */
5629         mimetext->data.mem = g_strdup(buf); 
5630         mimetext->type = MIMETYPE_TEXT;
5631         mimetext->subtype = g_strdup("plain");
5632         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5633                             g_strdup(out_codeset));
5634                             
5635         /* protect trailing spaces when signing message */
5636         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5637             privacy_system_can_sign(compose->privacy_system)) {
5638                 encoding = ENC_QUOTED_PRINTABLE;
5639         }
5640         
5641         debug_print("main text: %zd bytes encoded as %s in %d\n",
5642                 strlen(buf), out_codeset, encoding);
5643
5644         /* check for line length limit */
5645         if (action == COMPOSE_WRITE_FOR_SEND &&
5646             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5647             check_line_length(buf, 1000, &line) < 0) {
5648                 AlertValue aval;
5649                 gchar *msg;
5650
5651                 msg = g_strdup_printf
5652                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5653                            "The contents of the message might be broken on the way to the delivery.\n"
5654                            "\n"
5655                            "Send it anyway?"), line + 1);
5656                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5657                 g_free(msg);
5658                 if (aval != G_ALERTALTERNATE) {
5659                         g_free(buf);
5660                         return -1;
5661                 }
5662         }
5663         
5664         if (encoding != ENC_UNKNOWN)
5665                 procmime_encode_content(mimetext, encoding);
5666
5667         /* append attachment parts */
5668         if (compose_use_attach(compose) && attach_parts) {
5669                 MimeInfo *mimempart;
5670                 gchar *boundary = NULL;
5671                 mimempart = procmime_mimeinfo_new();
5672                 mimempart->content = MIMECONTENT_EMPTY;
5673                 mimempart->type = MIMETYPE_MULTIPART;
5674                 mimempart->subtype = g_strdup("mixed");
5675
5676                 do {
5677                         g_free(boundary);
5678                         boundary = generate_mime_boundary(NULL);
5679                 } while (strstr(buf, boundary) != NULL);
5680
5681                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5682                                     boundary);
5683
5684                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5685
5686                 g_node_append(mimempart->node, mimetext->node);
5687                 g_node_append(mimemsg->node, mimempart->node);
5688
5689                 if (compose_add_attachments(compose, mimempart) < 0)
5690                         return -1;
5691         } else
5692                 g_node_append(mimemsg->node, mimetext->node);
5693
5694         g_free(buf);
5695
5696         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5697                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5698                 /* extract name and address */
5699                 if (strstr(spec, " <") && strstr(spec, ">")) {
5700                         from_addr = g_strdup(strrchr(spec, '<')+1);
5701                         *(strrchr(from_addr, '>')) = '\0';
5702                         from_name = g_strdup(spec);
5703                         *(strrchr(from_name, '<')) = '\0';
5704                 } else {
5705                         from_name = NULL;
5706                         from_addr = NULL;
5707                 }
5708                 g_free(spec);
5709         }
5710         /* sign message if sending */
5711         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5712             privacy_system_can_sign(compose->privacy_system))
5713                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5714                         compose->account, from_addr)) {
5715                         g_free(from_name);
5716                         g_free(from_addr);
5717                         return -2;
5718         }
5719         g_free(from_name);
5720         g_free(from_addr);
5721         procmime_write_mimeinfo(mimemsg, fp);
5722         
5723         procmime_mimeinfo_free_all(mimemsg);
5724
5725         return 0;
5726 }
5727
5728 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5729 {
5730         GtkTextBuffer *buffer;
5731         GtkTextIter start, end;
5732         FILE *fp;
5733         size_t len;
5734         gchar *chars, *tmp;
5735
5736         if ((fp = g_fopen(file, "wb")) == NULL) {
5737                 FILE_OP_ERROR(file, "fopen");
5738                 return -1;
5739         }
5740
5741         /* chmod for security */
5742         if (change_file_mode_rw(fp, file) < 0) {
5743                 FILE_OP_ERROR(file, "chmod");
5744                 g_warning("can't change file mode\n");
5745         }
5746
5747         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5748         gtk_text_buffer_get_start_iter(buffer, &start);
5749         gtk_text_buffer_get_end_iter(buffer, &end);
5750         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5751
5752         chars = conv_codeset_strdup
5753                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5754
5755         g_free(tmp);
5756         if (!chars) {
5757                 fclose(fp);
5758                 claws_unlink(file);
5759                 return -1;
5760         }
5761         /* write body */
5762         len = strlen(chars);
5763         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5764                 FILE_OP_ERROR(file, "fwrite");
5765                 g_free(chars);
5766                 fclose(fp);
5767                 claws_unlink(file);
5768                 return -1;
5769         }
5770
5771         g_free(chars);
5772
5773         if (fclose(fp) == EOF) {
5774                 FILE_OP_ERROR(file, "fclose");
5775                 claws_unlink(file);
5776                 return -1;
5777         }
5778         return 0;
5779 }
5780
5781 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5782 {
5783         FolderItem *item;
5784         MsgInfo *msginfo = compose->targetinfo;
5785
5786         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5787         if (!msginfo) return -1;
5788
5789         if (!force && MSG_IS_LOCKED(msginfo->flags))
5790                 return 0;
5791
5792         item = msginfo->folder;
5793         cm_return_val_if_fail(item != NULL, -1);
5794
5795         if (procmsg_msg_exist(msginfo) &&
5796             (folder_has_parent_of_type(item, F_QUEUE) ||
5797              folder_has_parent_of_type(item, F_DRAFT) 
5798              || msginfo == compose->autosaved_draft)) {
5799                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5800                         g_warning("can't remove the old message\n");
5801                         return -1;
5802                 } else {
5803                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5804                 }
5805         }
5806
5807         return 0;
5808 }
5809
5810 static void compose_remove_draft(Compose *compose)
5811 {
5812         FolderItem *drafts;
5813         MsgInfo *msginfo = compose->targetinfo;
5814         drafts = account_get_special_folder(compose->account, F_DRAFT);
5815
5816         if (procmsg_msg_exist(msginfo)) {
5817                 folder_item_remove_msg(drafts, msginfo->msgnum);
5818         }
5819
5820 }
5821
5822 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5823                    gboolean remove_reedit_target)
5824 {
5825         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5826 }
5827
5828 static gboolean compose_warn_encryption(Compose *compose)
5829 {
5830         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5831         AlertValue val = G_ALERTALTERNATE;
5832         
5833         if (warning == NULL)
5834                 return TRUE;
5835
5836         val = alertpanel_full(_("Encryption warning"), warning,
5837                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5838                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5839         if (val & G_ALERTDISABLE) {
5840                 val &= ~G_ALERTDISABLE;
5841                 if (val == G_ALERTALTERNATE)
5842                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5843                                 TRUE);
5844         }
5845
5846         if (val == G_ALERTALTERNATE) {
5847                 return TRUE;
5848         } else {
5849                 return FALSE;
5850         } 
5851 }
5852
5853 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5854                               gchar **msgpath, gboolean check_subject,
5855                               gboolean remove_reedit_target)
5856 {
5857         FolderItem *queue;
5858         gchar *tmp;
5859         FILE *fp;
5860         GSList *cur;
5861         gint num;
5862         PrefsAccount *mailac = NULL, *newsac = NULL;
5863         gboolean err = FALSE;
5864
5865         debug_print("queueing message...\n");
5866         cm_return_val_if_fail(compose->account != NULL, -1);
5867
5868         if (compose_check_entries(compose, check_subject) == FALSE) {
5869                 if (compose->batch) {
5870                         gtk_widget_show_all(compose->window);
5871                 }
5872                 return -1;
5873         }
5874
5875         if (!compose->to_list && !compose->newsgroup_list) {
5876                 g_warning("can't get recipient list.");
5877                 return -1;
5878         }
5879
5880         if (compose->to_list) {
5881                 if (compose->account->protocol != A_NNTP)
5882                         mailac = compose->account;
5883                 else if (cur_account && cur_account->protocol != A_NNTP)
5884                         mailac = cur_account;
5885                 else if (!(mailac = compose_current_mail_account())) {
5886                         alertpanel_error(_("No account for sending mails available!"));
5887                         return -1;
5888                 }
5889         }
5890
5891         if (compose->newsgroup_list) {
5892                 if (compose->account->protocol == A_NNTP)
5893                         newsac = compose->account;
5894                 else {
5895                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5896                         return -1;
5897                 }                       
5898         }
5899
5900         /* write queue header */
5901         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5902                               G_DIR_SEPARATOR, compose, (guint) rand());
5903         debug_print("queuing to %s\n", tmp);
5904         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5905                 FILE_OP_ERROR(tmp, "fopen");
5906                 g_free(tmp);
5907                 return -2;
5908         }
5909
5910         if (change_file_mode_rw(fp, tmp) < 0) {
5911                 FILE_OP_ERROR(tmp, "chmod");
5912                 g_warning("can't change file mode\n");
5913         }
5914
5915         /* queueing variables */
5916         err |= (fprintf(fp, "AF:\n") < 0);
5917         err |= (fprintf(fp, "NF:0\n") < 0);
5918         err |= (fprintf(fp, "PS:10\n") < 0);
5919         err |= (fprintf(fp, "SRH:1\n") < 0);
5920         err |= (fprintf(fp, "SFN:\n") < 0);
5921         err |= (fprintf(fp, "DSR:\n") < 0);
5922         if (compose->msgid)
5923                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5924         else
5925                 err |= (fprintf(fp, "MID:\n") < 0);
5926         err |= (fprintf(fp, "CFG:\n") < 0);
5927         err |= (fprintf(fp, "PT:0\n") < 0);
5928         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5929         err |= (fprintf(fp, "RQ:\n") < 0);
5930         if (mailac)
5931                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5932         else
5933                 err |= (fprintf(fp, "SSV:\n") < 0);
5934         if (newsac)
5935                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5936         else
5937                 err |= (fprintf(fp, "NSV:\n") < 0);
5938         err |= (fprintf(fp, "SSH:\n") < 0);
5939         /* write recepient list */
5940         if (compose->to_list) {
5941                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5942                 for (cur = compose->to_list->next; cur != NULL;
5943                      cur = cur->next)
5944                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5945                 err |= (fprintf(fp, "\n") < 0);
5946         }
5947         /* write newsgroup list */
5948         if (compose->newsgroup_list) {
5949                 err |= (fprintf(fp, "NG:") < 0);
5950                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5951                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5952                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5953                 err |= (fprintf(fp, "\n") < 0);
5954         }
5955         /* Sylpheed account IDs */
5956         if (mailac)
5957                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5958         if (newsac)
5959                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5960
5961         
5962         if (compose->privacy_system != NULL) {
5963                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5964                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5965                 if (compose->use_encryption) {
5966                         gchar *encdata;
5967                         if (!compose_warn_encryption(compose)) {
5968                                 fclose(fp);
5969                                 claws_unlink(tmp);
5970                                 g_free(tmp);
5971                                 return -6;
5972                         }
5973                         if (mailac && mailac->encrypt_to_self) {
5974                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5975                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5976                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5977                                 g_slist_free(tmp_list);
5978                         } else {
5979                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5980                         }
5981                         if (encdata != NULL) {
5982                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5983                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5984                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5985                                                 encdata) < 0);
5986                                 } /* else we finally dont want to encrypt */
5987                         } else {
5988                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5989                                 /* and if encdata was null, it means there's been a problem in 
5990                                  * key selection */
5991                                 if (err == TRUE)
5992                                         g_warning("failed to write queue message");
5993                                 fclose(fp);
5994                                 claws_unlink(tmp);
5995                                 g_free(tmp);
5996                                 return -5;
5997                         }
5998                         g_free(encdata);
5999                 }
6000         }
6001
6002         /* Save copy folder */
6003         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6004                 gchar *savefolderid;
6005                 
6006                 savefolderid = compose_get_save_to(compose);
6007                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
6008                 g_free(savefolderid);
6009         }
6010         /* Save copy folder */
6011         if (compose->return_receipt) {
6012                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
6013         }
6014         /* Message-ID of message replying to */
6015         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
6016                 gchar *folderid = NULL;
6017
6018                 if (compose->replyinfo->folder)
6019                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
6020                 if (folderid == NULL)
6021                         folderid = g_strdup("NULL");
6022
6023                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
6024                 g_free(folderid);
6025         }
6026         /* Message-ID of message forwarding to */
6027         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
6028                 gchar *folderid = NULL;
6029                 
6030                 if (compose->fwdinfo->folder)
6031                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
6032                 if (folderid == NULL)
6033                         folderid = g_strdup("NULL");
6034
6035                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
6036                 g_free(folderid);
6037         }
6038
6039         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
6040         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
6041
6042         /* end of headers */
6043         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
6044
6045         if (compose->redirect_filename != NULL) {
6046                 if (compose_redirect_write_to_file(compose, fp) < 0) {
6047                         fclose(fp);
6048                         claws_unlink(tmp);
6049                         g_free(tmp);
6050                         return -2;
6051                 }
6052         } else {
6053                 gint result = 0;
6054                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
6055                         fclose(fp);
6056                         claws_unlink(tmp);
6057                         g_free(tmp);
6058                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6059                 }
6060         }
6061         if (err == TRUE) {
6062                 g_warning("failed to write queue message\n");
6063                 fclose(fp);
6064                 claws_unlink(tmp);
6065                 g_free(tmp);
6066                 return -2;
6067         }
6068         if (fclose(fp) == EOF) {
6069                 FILE_OP_ERROR(tmp, "fclose");
6070                 claws_unlink(tmp);
6071                 g_free(tmp);
6072                 return -2;
6073         }
6074
6075         if (item && *item) {
6076                 queue = *item;
6077         } else {
6078                 queue = account_get_special_folder(compose->account, F_QUEUE);
6079         }
6080         if (!queue) {
6081                 g_warning("can't find queue folder\n");
6082                 claws_unlink(tmp);
6083                 g_free(tmp);
6084                 return -1;
6085         }
6086         folder_item_scan(queue);
6087         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6088                 g_warning("can't queue the message\n");
6089                 claws_unlink(tmp);
6090                 g_free(tmp);
6091                 return -1;
6092         }
6093         
6094         if (msgpath == NULL) {
6095                 claws_unlink(tmp);
6096                 g_free(tmp);
6097         } else
6098                 *msgpath = tmp;
6099
6100         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6101                 compose_remove_reedit_target(compose, FALSE);
6102         }
6103
6104         if ((msgnum != NULL) && (item != NULL)) {
6105                 *msgnum = num;
6106                 *item = queue;
6107         }
6108
6109         return 0;
6110 }
6111
6112 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6113 {
6114         AttachInfo *ainfo;
6115         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6116         MimeInfo *mimepart;
6117         struct stat statbuf;
6118         gchar *type, *subtype;
6119         GtkTreeModel *model;
6120         GtkTreeIter iter;
6121
6122         model = gtk_tree_view_get_model(tree_view);
6123         
6124         if (!gtk_tree_model_get_iter_first(model, &iter))
6125                 return 0;
6126         do {
6127                 gtk_tree_model_get(model, &iter,
6128                                    COL_DATA, &ainfo,
6129                                    -1);
6130                 
6131                 if (!is_file_exist(ainfo->file)) {
6132                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6133                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6134                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6135                         g_free(msg);
6136                         if (val == G_ALERTDEFAULT) {
6137                                 return -1;
6138                         }
6139                         continue;
6140                 }
6141                 if (g_stat(ainfo->file, &statbuf) < 0)
6142                         return -1;
6143
6144                 mimepart = procmime_mimeinfo_new();
6145                 mimepart->content = MIMECONTENT_FILE;
6146                 mimepart->data.filename = g_strdup(ainfo->file);
6147                 mimepart->tmp = FALSE; /* or we destroy our attachment */
6148                 mimepart->offset = 0;
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         
6832         headerentry = g_new0(ComposeHeaderEntry, 1);
6833
6834         /* Combo box model */
6835         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6836 #if !GTK_CHECK_VERSION(2, 24, 0)
6837         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6838 #endif
6839         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6840                         COMPOSE_TO);
6841         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6842                         COMPOSE_CC);
6843         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6844                         COMPOSE_BCC);
6845         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6846                         COMPOSE_NEWSGROUPS);                    
6847         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6848                         COMPOSE_REPLYTO);
6849         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6850                         COMPOSE_FOLLOWUPTO);
6851         compose_add_extra_header_entries(model);
6852
6853         /* Combo box */
6854 #if GTK_CHECK_VERSION(2, 24, 0)
6855         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6856         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6857         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6858         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6859         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6860 #endif
6861         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6862         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6863                          G_CALLBACK(compose_grab_focus_cb), compose);
6864         gtk_widget_show(combo);
6865
6866         /* Putting only the combobox child into focus chain of its parent causes
6867          * the parent to be skipped when changing focus via Tab or Shift+Tab.
6868          * This eliminates need to pres Tab twice in order to really get from the
6869          * combobox to next widget. */
6870         GList *l = NULL;
6871         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6872         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6873         g_list_free(l);
6874
6875         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6876                         compose->header_nextrow, compose->header_nextrow+1,
6877                         GTK_SHRINK, GTK_FILL, 0, 0);
6878         if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
6879                 const gchar *last_header_entry = gtk_entry_get_text(
6880                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6881                 string = headers;
6882                 while (*string != NULL) {
6883                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6884                                 standard_header = TRUE;
6885                         string++;
6886                 }
6887                 if (standard_header)
6888                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6889         }
6890         if (!compose->header_last || !standard_header) {
6891                 switch(compose->account->protocol) {
6892                         case A_NNTP:
6893                                 header = prefs_common_translated_header_name("Newsgroups:");
6894                                 break;
6895                         default:
6896                                 header = prefs_common_translated_header_name("To:");
6897                                 break;
6898                 }                                                                   
6899         }
6900         if (header)
6901                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6902
6903         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6904                          G_CALLBACK(compose_grab_focus_cb), compose);
6905
6906         /* Entry field with cleanup button */
6907         button = gtk_button_new();
6908         gtk_button_set_image(GTK_BUTTON(button),
6909                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6910         gtk_widget_show(button);
6911         CLAWS_SET_TIP(button,
6912                 _("Delete entry contents"));
6913         entry = gtk_entry_new(); 
6914         gtk_widget_show(entry);
6915         CLAWS_SET_TIP(entry,
6916                 _("Use <tab> to autocomplete from addressbook"));
6917         hbox = gtk_hbox_new (FALSE, 0);
6918         gtk_widget_show(hbox);
6919         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6920         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6921         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6922                         compose->header_nextrow, compose->header_nextrow+1,
6923                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6924
6925         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6926                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6927                          headerentry);
6928         g_signal_connect(G_OBJECT(entry), "changed", 
6929                          G_CALLBACK(compose_headerentry_changed_cb), 
6930                          headerentry);
6931         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6932                          G_CALLBACK(compose_grab_focus_cb), compose);
6933
6934         g_signal_connect(G_OBJECT(button), "clicked",
6935                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6936                          headerentry); 
6937                          
6938         /* email dnd */
6939         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6940                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6941                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6942         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6943                          G_CALLBACK(compose_header_drag_received_cb),
6944                          entry);
6945         g_signal_connect(G_OBJECT(entry), "drag-drop",
6946                          G_CALLBACK(compose_drag_drop),
6947                          compose);
6948         g_signal_connect(G_OBJECT(entry), "populate-popup",
6949                          G_CALLBACK(compose_entry_popup_extend),
6950                          NULL);
6951         
6952         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6953
6954         headerentry->compose = compose;
6955         headerentry->combo = combo;
6956         headerentry->entry = entry;
6957         headerentry->button = button;
6958         headerentry->hbox = hbox;
6959         headerentry->headernum = compose->header_nextrow;
6960         headerentry->type = PREF_NONE;
6961
6962         compose->header_nextrow++;
6963         compose->header_last = headerentry;             
6964         compose->header_list =
6965                 g_slist_append(compose->header_list,
6966                                headerentry);
6967 }
6968
6969 static void compose_add_header_entry(Compose *compose, const gchar *header,
6970                                 gchar *text, ComposePrefType pref_type) 
6971 {
6972         ComposeHeaderEntry *last_header = compose->header_last;
6973         gchar *tmp = g_strdup(text), *email;
6974         gboolean replyto_hdr;
6975         
6976         replyto_hdr = (!strcasecmp(header,
6977                                 prefs_common_translated_header_name("Reply-To:")) ||
6978                         !strcasecmp(header,
6979                                 prefs_common_translated_header_name("Followup-To:")) ||
6980                         !strcasecmp(header,
6981                                 prefs_common_translated_header_name("In-Reply-To:")));
6982                 
6983         extract_address(tmp);
6984         email = g_utf8_strdown(tmp, -1);
6985         
6986         if (replyto_hdr == FALSE &&
6987             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6988         {
6989                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6990                                 header, text, (gint) pref_type);
6991                 g_free(email);
6992                 g_free(tmp);
6993                 return;
6994         }
6995         
6996         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6997                 gtk_entry_set_text(GTK_ENTRY(
6998                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6999         else
7000                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
7001         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
7002         last_header->type = pref_type;
7003
7004         if (replyto_hdr == FALSE)
7005                 g_hash_table_insert(compose->email_hashtable, email,
7006                                     GUINT_TO_POINTER(1));
7007         else
7008                 g_free(email);
7009         
7010         g_free(tmp);
7011 }
7012
7013 static void compose_destroy_headerentry(Compose *compose, 
7014                                         ComposeHeaderEntry *headerentry)
7015 {
7016         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7017         gchar *email;
7018
7019         extract_address(text);
7020         email = g_utf8_strdown(text, -1);
7021         g_hash_table_remove(compose->email_hashtable, email);
7022         g_free(text);
7023         g_free(email);
7024         
7025         gtk_widget_destroy(headerentry->combo);
7026         gtk_widget_destroy(headerentry->entry);
7027         gtk_widget_destroy(headerentry->button);
7028         gtk_widget_destroy(headerentry->hbox);
7029         g_free(headerentry);
7030 }
7031
7032 static void compose_remove_header_entries(Compose *compose) 
7033 {
7034         GSList *list;
7035         for (list = compose->header_list; list; list = list->next)
7036                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7037
7038         compose->header_last = NULL;
7039         g_slist_free(compose->header_list);
7040         compose->header_list = NULL;
7041         compose->header_nextrow = 1;
7042         compose_create_header_entry(compose);
7043 }
7044
7045 static GtkWidget *compose_create_header(Compose *compose) 
7046 {
7047         GtkWidget *from_optmenu_hbox;
7048         GtkWidget *header_scrolledwin_main;
7049         GtkWidget *header_table_main;
7050         GtkWidget *header_scrolledwin;
7051         GtkWidget *header_table;
7052
7053         /* parent with account selection and from header */
7054         header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
7055         gtk_widget_show(header_scrolledwin_main);
7056         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7057
7058         header_table_main = gtk_table_new(2, 2, FALSE);
7059         gtk_widget_show(header_table_main);
7060         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7061         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
7062         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
7063
7064         from_optmenu_hbox = compose_account_option_menu_create(compose);
7065         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7066                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7067
7068         /* child with header labels and entries */
7069         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7070         gtk_widget_show(header_scrolledwin);
7071         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7072
7073         header_table = gtk_table_new(2, 2, FALSE);
7074         gtk_widget_show(header_table);
7075         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
7076         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7077         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
7078
7079         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7080                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7081
7082         compose->header_table = header_table;
7083         compose->header_list = NULL;
7084         compose->header_nextrow = 0;
7085
7086         compose_create_header_entry(compose);
7087
7088         compose->table = NULL;
7089
7090         return header_scrolledwin_main;
7091 }
7092
7093 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7094 {
7095         Compose *compose = (Compose *)data;
7096         GdkEventButton event;
7097         
7098         event.button = 3;
7099         event.time = gtk_get_current_event_time();
7100
7101         return attach_button_pressed(compose->attach_clist, &event, compose);
7102 }
7103
7104 static GtkWidget *compose_create_attach(Compose *compose)
7105 {
7106         GtkWidget *attach_scrwin;
7107         GtkWidget *attach_clist;
7108
7109         GtkListStore *store;
7110         GtkCellRenderer *renderer;
7111         GtkTreeViewColumn *column;
7112         GtkTreeSelection *selection;
7113
7114         /* attachment list */
7115         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7116         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7117                                        GTK_POLICY_AUTOMATIC,
7118                                        GTK_POLICY_AUTOMATIC);
7119         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7120
7121         store = gtk_list_store_new(N_ATTACH_COLS, 
7122                                    G_TYPE_STRING,
7123                                    G_TYPE_STRING,
7124                                    G_TYPE_STRING,
7125                                    G_TYPE_STRING,
7126                                    G_TYPE_POINTER,
7127                                    G_TYPE_AUTO_POINTER,
7128                                    -1);
7129         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7130                                         (GTK_TREE_MODEL(store)));
7131         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7132         g_object_unref(store);
7133         
7134         renderer = gtk_cell_renderer_text_new();
7135         column = gtk_tree_view_column_new_with_attributes
7136                         (_("Mime type"), renderer, "text", 
7137                          COL_MIMETYPE, NULL);
7138         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7139         
7140         renderer = gtk_cell_renderer_text_new();
7141         column = gtk_tree_view_column_new_with_attributes
7142                         (_("Size"), renderer, "text", 
7143                          COL_SIZE, NULL);
7144         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7145         
7146         renderer = gtk_cell_renderer_text_new();
7147         column = gtk_tree_view_column_new_with_attributes
7148                         (_("Name"), renderer, "text", 
7149                          COL_NAME, NULL);
7150         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7151
7152         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7153                                      prefs_common.use_stripes_everywhere);
7154         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7155         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7156
7157         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7158                          G_CALLBACK(attach_selected), compose);
7159         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7160                          G_CALLBACK(attach_button_pressed), compose);
7161         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7162                          G_CALLBACK(popup_attach_button_pressed), compose);
7163         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7164                          G_CALLBACK(attach_key_pressed), compose);
7165
7166         /* drag and drop */
7167         gtk_drag_dest_set(attach_clist,
7168                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7169                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7170                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7171         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7172                          G_CALLBACK(compose_attach_drag_received_cb),
7173                          compose);
7174         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7175                          G_CALLBACK(compose_drag_drop),
7176                          compose);
7177
7178         compose->attach_scrwin = attach_scrwin;
7179         compose->attach_clist  = attach_clist;
7180
7181         return attach_scrwin;
7182 }
7183
7184 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7185 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7186
7187 static GtkWidget *compose_create_others(Compose *compose)
7188 {
7189         GtkWidget *table;
7190         GtkWidget *savemsg_checkbtn;
7191         GtkWidget *savemsg_combo;
7192         GtkWidget *savemsg_select;
7193         
7194         guint rowcount = 0;
7195         gchar *folderidentifier;
7196
7197         /* Table for settings */
7198         table = gtk_table_new(3, 1, FALSE);
7199         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7200         gtk_widget_show(table);
7201         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7202         rowcount = 0;
7203
7204         /* Save Message to folder */
7205         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7206         gtk_widget_show(savemsg_checkbtn);
7207         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7208         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7209                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7210         }
7211         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7212                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7213
7214 #if !GTK_CHECK_VERSION(2, 24, 0)
7215         savemsg_combo = gtk_combo_box_entry_new_text();
7216 #else
7217         savemsg_combo = gtk_combo_box_text_new_with_entry();
7218 #endif
7219         compose->savemsg_checkbtn = savemsg_checkbtn;
7220         compose->savemsg_combo = savemsg_combo;
7221         gtk_widget_show(savemsg_combo);
7222
7223         if (prefs_common.compose_save_to_history)
7224 #if !GTK_CHECK_VERSION(2, 24, 0)
7225                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7226                                 prefs_common.compose_save_to_history);
7227 #else
7228                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7229                                 prefs_common.compose_save_to_history);
7230 #endif
7231         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7232         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7233         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7234                          G_CALLBACK(compose_grab_focus_cb), compose);
7235         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7236                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7237                                   (compose->account, F_OUTBOX));
7238                 compose_set_save_to(compose, folderidentifier);
7239                 g_free(folderidentifier);
7240         }
7241
7242         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7243         gtk_widget_show(savemsg_select);
7244         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7245         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7246                          G_CALLBACK(compose_savemsg_select_cb),
7247                          compose);
7248
7249         return table;   
7250 }
7251
7252 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7253 {
7254         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7255                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7256 }
7257
7258 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7259 {
7260         FolderItem *dest;
7261         gchar * path;
7262
7263         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7264         if (!dest) return;
7265
7266         path = folder_item_get_identifier(dest);
7267
7268         compose_set_save_to(compose, path);
7269         g_free(path);
7270 }
7271
7272 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7273                                   GdkAtom clip, GtkTextIter *insert_place);
7274
7275
7276 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7277                                        Compose *compose)
7278 {
7279         gint prev_autowrap;
7280         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7281 #if USE_ENCHANT
7282         if (event->button == 3) {
7283                 GtkTextIter iter;
7284                 GtkTextIter sel_start, sel_end;
7285                 gboolean stuff_selected;
7286                 gint x, y;
7287                 /* move the cursor to allow GtkAspell to check the word
7288                  * under the mouse */
7289                 if (event->x && event->y) {
7290                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7291                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7292                                 &x, &y);
7293                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7294                                 &iter, x, y);
7295                 } else {
7296                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7297                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7298                 }
7299                 /* get selection */
7300                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7301                                 buffer,
7302                                 &sel_start, &sel_end);
7303
7304                 gtk_text_buffer_place_cursor (buffer, &iter);
7305                 /* reselect stuff */
7306                 if (stuff_selected 
7307                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7308                         gtk_text_buffer_select_range(buffer,
7309                                 &sel_start, &sel_end);
7310                 }
7311                 return FALSE; /* pass the event so that the right-click goes through */
7312         }
7313 #endif
7314         if (event->button == 2) {
7315                 GtkTextIter iter;
7316                 gint x, y;
7317                 BLOCK_WRAP();
7318                 
7319                 /* get the middle-click position to paste at the correct place */
7320                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7321                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7322                         &x, &y);
7323                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7324                         &iter, x, y);
7325                 
7326                 entry_paste_clipboard(compose, text, 
7327                                 prefs_common.linewrap_pastes,
7328                                 GDK_SELECTION_PRIMARY, &iter);
7329                 UNBLOCK_WRAP();
7330                 return TRUE;
7331         }
7332         return FALSE;
7333 }
7334
7335 #if USE_ENCHANT
7336 static void compose_spell_menu_changed(void *data)
7337 {
7338         Compose *compose = (Compose *)data;
7339         GSList *items;
7340         GtkWidget *menuitem;
7341         GtkWidget *parent_item;
7342         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7343         GSList *spell_menu;
7344
7345         if (compose->gtkaspell == NULL)
7346                 return;
7347
7348         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7349                         "/Menu/Spelling/Options");
7350
7351         /* setting the submenu removes /Spelling/Options from the factory 
7352          * so we need to save it */
7353
7354         if (parent_item == NULL) {
7355                 parent_item = compose->aspell_options_menu;
7356                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7357         } else
7358                 compose->aspell_options_menu = parent_item;
7359
7360         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7361
7362         spell_menu = g_slist_reverse(spell_menu);
7363         for (items = spell_menu;
7364              items; items = items->next) {
7365                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7366                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7367                 gtk_widget_show(GTK_WIDGET(menuitem));
7368         }
7369         g_slist_free(spell_menu);
7370
7371         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7372         gtk_widget_show(parent_item);
7373 }
7374
7375 static void compose_dict_changed(void *data)
7376 {
7377         Compose *compose = (Compose *) data;
7378
7379         if(!compose->gtkaspell)
7380                 return; 
7381         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7382                 return;
7383
7384         gtkaspell_highlight_all(compose->gtkaspell);
7385         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7386 }
7387 #endif
7388
7389 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7390 {
7391         Compose *compose = (Compose *)data;
7392         GdkEventButton event;
7393         
7394         event.button = 3;
7395         event.time = gtk_get_current_event_time();
7396         event.x = 0;
7397         event.y = 0;
7398
7399         return text_clicked(compose->text, &event, compose);
7400 }
7401
7402 static gboolean compose_force_window_origin = TRUE;
7403 static Compose *compose_create(PrefsAccount *account,
7404                                                  FolderItem *folder,
7405                                                  ComposeMode mode,
7406                                                  gboolean batch)
7407 {
7408         Compose   *compose;
7409         GtkWidget *window;
7410         GtkWidget *vbox;
7411         GtkWidget *menubar;
7412         GtkWidget *handlebox;
7413
7414         GtkWidget *notebook;
7415         
7416         GtkWidget *attach_hbox;
7417         GtkWidget *attach_lab1;
7418         GtkWidget *attach_lab2;
7419
7420         GtkWidget *vbox2;
7421
7422         GtkWidget *label;
7423         GtkWidget *subject_hbox;
7424         GtkWidget *subject_frame;
7425         GtkWidget *subject_entry;
7426         GtkWidget *subject;
7427         GtkWidget *paned;
7428
7429         GtkWidget *edit_vbox;
7430         GtkWidget *ruler_hbox;
7431         GtkWidget *ruler;
7432         GtkWidget *scrolledwin;
7433         GtkWidget *text;
7434         GtkTextBuffer *buffer;
7435         GtkClipboard *clipboard;
7436
7437         UndoMain *undostruct;
7438
7439         GtkWidget *popupmenu;
7440         GtkWidget *tmpl_menu;
7441         GtkActionGroup *action_group = NULL;
7442
7443 #if USE_ENCHANT
7444         GtkAspell * gtkaspell = NULL;
7445 #endif
7446
7447         static GdkGeometry geometry;
7448
7449         cm_return_val_if_fail(account != NULL, NULL);
7450
7451         debug_print("Creating compose window...\n");
7452         compose = g_new0(Compose, 1);
7453
7454         compose->batch = batch;
7455         compose->account = account;
7456         compose->folder = folder;
7457         
7458         compose->mutex = cm_mutex_new();
7459         compose->set_cursor_pos = -1;
7460
7461         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7462
7463         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7464         gtk_widget_set_size_request(window, prefs_common.compose_width,
7465                                         prefs_common.compose_height);
7466
7467         if (!geometry.max_width) {
7468                 geometry.max_width = gdk_screen_width();
7469                 geometry.max_height = gdk_screen_height();
7470         }
7471
7472         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7473                                       &geometry, GDK_HINT_MAX_SIZE);
7474         if (!geometry.min_width) {
7475                 geometry.min_width = 600;
7476                 geometry.min_height = 440;
7477         }
7478         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7479                                       &geometry, GDK_HINT_MIN_SIZE);
7480
7481 #ifndef GENERIC_UMPC    
7482         if (compose_force_window_origin)
7483                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7484                                  prefs_common.compose_y);
7485 #endif
7486         g_signal_connect(G_OBJECT(window), "delete_event",
7487                          G_CALLBACK(compose_delete_cb), compose);
7488         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7489         gtk_widget_realize(window);
7490
7491         gtkut_widget_set_composer_icon(window);
7492
7493         vbox = gtk_vbox_new(FALSE, 0);
7494         gtk_container_add(GTK_CONTAINER(window), vbox);
7495
7496         compose->ui_manager = gtk_ui_manager_new();
7497         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7498                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7499         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7500                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7501         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7502                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7503         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7504                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7505         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7506                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7507
7508         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7509
7510         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7511         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7512 #ifdef USE_ENCHANT
7513         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7514 #endif
7515         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7516         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7517         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7518
7519 /* Compose menu */
7520         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7521         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7522         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7523         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7524         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7525         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7526         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7527         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7528         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7529         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7530         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7531         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7532         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7533
7534 /* Edit menu */
7535         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7536         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7537         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7538
7539         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7540         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7541         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7542
7543         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7544         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7545         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7546         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7547
7548         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7549
7550         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7551         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7552         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7553         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7554         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7555         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7556         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7557         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7558         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7559         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7560         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7561         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7562         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7563         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7564         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7565
7566         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7567
7568         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7569         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7570         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7571         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7572         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7573
7574         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7575
7576         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7577
7578 #if USE_ENCHANT
7579 /* Spelling menu */
7580         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7581         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7582         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7583         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7584         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7585         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7586 #endif
7587
7588 /* Options menu */
7589         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7590         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7591         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7592         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7593         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7594
7595         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7596         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7597         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7598         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7599         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7600
7601         
7602         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7603         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7604         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7605         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7606         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7607         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7608         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7609
7610         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7611         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7612         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7613         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7614         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7615
7616         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7617
7618         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7619         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7620         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7621         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7622         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7623
7624         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7625         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)
7626         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)
7627         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7628
7629         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7630
7631         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7632         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)
7633         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)
7634
7635         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7636
7637         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7638         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)
7639         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7640
7641         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7642         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)
7643         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7644
7645         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7646
7647         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7648         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)
7649         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7650         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7651         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7652
7653         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7654         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)
7655         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)
7656         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7657         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7658
7659         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7660         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7661         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7662         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7663         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7664         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7665
7666         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7667         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7668         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)
7669
7670         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7671         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7672         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7673 /* phew. */
7674
7675 /* Tools menu */
7676         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7677         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7678         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7679         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7680         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7681         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7682
7683 /* Help menu */
7684         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7685
7686         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7687         gtk_widget_show_all(menubar);
7688
7689         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7690         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7691
7692         if (prefs_common.toolbar_detachable) {
7693                 handlebox = gtk_handle_box_new();
7694         } else {
7695                 handlebox = gtk_hbox_new(FALSE, 0);
7696         }
7697         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7698
7699         gtk_widget_realize(handlebox);
7700         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7701                                           (gpointer)compose);
7702
7703         vbox2 = gtk_vbox_new(FALSE, 2);
7704         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7705         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7706         
7707         /* Notebook */
7708         notebook = gtk_notebook_new();
7709         gtk_widget_show(notebook);
7710
7711         /* header labels and entries */
7712         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7713                         compose_create_header(compose),
7714                         gtk_label_new_with_mnemonic(_("Hea_der")));
7715         /* attachment list */
7716         attach_hbox = gtk_hbox_new(FALSE, 0);
7717         gtk_widget_show(attach_hbox);
7718         
7719         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7720         gtk_widget_show(attach_lab1);
7721         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7722         
7723         attach_lab2 = gtk_label_new("");
7724         gtk_widget_show(attach_lab2);
7725         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7726         
7727         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7728                         compose_create_attach(compose),
7729                         attach_hbox);
7730         /* Others Tab */
7731         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7732                         compose_create_others(compose),
7733                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7734
7735         /* Subject */
7736         subject_hbox = gtk_hbox_new(FALSE, 0);
7737         gtk_widget_show(subject_hbox);
7738
7739         subject_frame = gtk_frame_new(NULL);
7740         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7741         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7742         gtk_widget_show(subject_frame);
7743
7744         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7745         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7746         gtk_widget_show(subject);
7747
7748         label = gtk_label_new_with_mnemonic(_("_Subject:"));
7749         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7750         gtk_widget_show(label);
7751
7752 #ifdef USE_ENCHANT
7753         subject_entry = claws_spell_entry_new();
7754 #else
7755         subject_entry = gtk_entry_new();
7756 #endif
7757         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7758         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7759                          G_CALLBACK(compose_grab_focus_cb), compose);
7760         gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
7761         gtk_widget_show(subject_entry);
7762         compose->subject_entry = subject_entry;
7763         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7764         
7765         edit_vbox = gtk_vbox_new(FALSE, 0);
7766
7767         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7768
7769         /* ruler */
7770         ruler_hbox = gtk_hbox_new(FALSE, 0);
7771         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7772
7773         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7774         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7775         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7776                            BORDER_WIDTH);
7777
7778         /* text widget */
7779         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7780         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7781                                        GTK_POLICY_AUTOMATIC,
7782                                        GTK_POLICY_AUTOMATIC);
7783         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7784                                             GTK_SHADOW_IN);
7785         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7786
7787         text = gtk_text_view_new();
7788         if (prefs_common.show_compose_margin) {
7789                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7790                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7791         }
7792         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7793         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7794         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7795         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7796         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7797         
7798         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7799         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7800                                G_CALLBACK(compose_edit_size_alloc),
7801                                ruler);
7802         g_signal_connect(G_OBJECT(buffer), "changed",
7803                          G_CALLBACK(compose_changed_cb), compose);
7804         g_signal_connect(G_OBJECT(text), "grab_focus",
7805                          G_CALLBACK(compose_grab_focus_cb), compose);
7806         g_signal_connect(G_OBJECT(buffer), "insert_text",
7807                          G_CALLBACK(text_inserted), compose);
7808         g_signal_connect(G_OBJECT(text), "button_press_event",
7809                          G_CALLBACK(text_clicked), compose);
7810         g_signal_connect(G_OBJECT(text), "popup-menu",
7811                          G_CALLBACK(compose_popup_menu), compose);
7812         g_signal_connect(G_OBJECT(subject_entry), "changed",
7813                         G_CALLBACK(compose_changed_cb), compose);
7814         g_signal_connect(G_OBJECT(subject_entry), "activate",
7815                         G_CALLBACK(compose_subject_entry_activated), compose);
7816
7817         /* drag and drop */
7818         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7819                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7820                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7821         g_signal_connect(G_OBJECT(text), "drag_data_received",
7822                          G_CALLBACK(compose_insert_drag_received_cb),
7823                          compose);
7824         g_signal_connect(G_OBJECT(text), "drag-drop",
7825                          G_CALLBACK(compose_drag_drop),
7826                          compose);
7827         g_signal_connect(G_OBJECT(text), "key-press-event",
7828                          G_CALLBACK(completion_set_focus_to_subject),
7829                          compose);
7830         gtk_widget_show_all(vbox);
7831
7832         /* pane between attach clist and text */
7833         paned = gtk_vpaned_new();
7834         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7835         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
7836         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
7837         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
7838         g_signal_connect(G_OBJECT(notebook), "size_allocate",
7839                          G_CALLBACK(compose_notebook_size_alloc), paned);
7840
7841         gtk_widget_show_all(paned);
7842
7843
7844         if (prefs_common.textfont) {
7845                 PangoFontDescription *font_desc;
7846
7847                 font_desc = pango_font_description_from_string
7848                         (prefs_common.textfont);
7849                 if (font_desc) {
7850                         gtk_widget_modify_font(text, font_desc);
7851                         pango_font_description_free(font_desc);
7852                 }
7853         }
7854
7855         gtk_action_group_add_actions(action_group, compose_popup_entries,
7856                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7857         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7858         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7859         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7860         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7861         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7862         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7863         
7864         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7865
7866         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7867         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7868         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7869
7870         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7871
7872         undostruct = undo_init(text);
7873         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7874                                    compose);
7875
7876         address_completion_start(window);
7877
7878         compose->window        = window;
7879         compose->vbox          = vbox;
7880         compose->menubar       = menubar;
7881         compose->handlebox     = handlebox;
7882
7883         compose->vbox2         = vbox2;
7884
7885         compose->paned = paned;
7886
7887         compose->attach_label  = attach_lab2;
7888
7889         compose->notebook      = notebook;
7890         compose->edit_vbox     = edit_vbox;
7891         compose->ruler_hbox    = ruler_hbox;
7892         compose->ruler         = ruler;
7893         compose->scrolledwin   = scrolledwin;
7894         compose->text          = text;
7895
7896         compose->focused_editable = NULL;
7897
7898         compose->popupmenu    = popupmenu;
7899
7900         compose->tmpl_menu = tmpl_menu;
7901
7902         compose->mode = mode;
7903         compose->rmode = mode;
7904
7905         compose->targetinfo = NULL;
7906         compose->replyinfo  = NULL;
7907         compose->fwdinfo    = NULL;
7908
7909         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7910                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7911         
7912         compose->replyto     = NULL;
7913         compose->cc          = NULL;
7914         compose->bcc         = NULL;
7915         compose->followup_to = NULL;
7916
7917         compose->ml_post     = NULL;
7918
7919         compose->inreplyto   = NULL;
7920         compose->references  = NULL;
7921         compose->msgid       = NULL;
7922         compose->boundary    = NULL;
7923
7924         compose->autowrap       = prefs_common.autowrap;
7925         compose->autoindent     = prefs_common.auto_indent;
7926         compose->use_signing    = FALSE;
7927         compose->use_encryption = FALSE;
7928         compose->privacy_system = NULL;
7929
7930         compose->modified = FALSE;
7931
7932         compose->return_receipt = FALSE;
7933
7934         compose->to_list        = NULL;
7935         compose->newsgroup_list = NULL;
7936
7937         compose->undostruct = undostruct;
7938
7939         compose->sig_str = NULL;
7940
7941         compose->exteditor_file    = NULL;
7942         compose->exteditor_pid     = -1;
7943         compose->exteditor_tag     = -1;
7944         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
7945
7946         compose->folder_update_callback_id =
7947                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
7948                                 compose_update_folder_hook,
7949                                 (gpointer) compose);
7950
7951 #if USE_ENCHANT
7952         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7953         if (mode != COMPOSE_REDIRECT) {
7954                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7955                     strcmp(prefs_common.dictionary, "")) {
7956                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7957                                                   prefs_common.alt_dictionary,
7958                                                   conv_get_locale_charset_str(),
7959                                                   prefs_common.misspelled_col,
7960                                                   prefs_common.check_while_typing,
7961                                                   prefs_common.recheck_when_changing_dict,
7962                                                   prefs_common.use_alternate,
7963                                                   prefs_common.use_both_dicts,
7964                                                   GTK_TEXT_VIEW(text),
7965                                                   GTK_WINDOW(compose->window),
7966                                                   compose_dict_changed,
7967                                                   compose_spell_menu_changed,
7968                                                   compose);
7969                         if (!gtkaspell) {
7970                                 alertpanel_error(_("Spell checker could not "
7971                                                 "be started.\n%s"),
7972                                                 gtkaspell_checkers_strerror());
7973                                 gtkaspell_checkers_reset_error();
7974                         } else {
7975                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7976                         }
7977                 }
7978         }
7979         compose->gtkaspell = gtkaspell;
7980         compose_spell_menu_changed(compose);
7981         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7982 #endif
7983
7984         compose_select_account(compose, account, TRUE);
7985
7986         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7987         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7988
7989         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7990                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7991
7992         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7993                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7994         
7995         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7996                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7997
7998         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7999         if (account->protocol != A_NNTP)
8000                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8001                                 prefs_common_translated_header_name("To:"));
8002         else
8003                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8004                                 prefs_common_translated_header_name("Newsgroups:"));
8005
8006 #ifndef USE_NEW_ADDRBOOK
8007         addressbook_set_target_compose(compose);
8008 #endif  
8009         if (mode != COMPOSE_REDIRECT)
8010                 compose_set_template_menu(compose);
8011         else {
8012                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8013         }
8014
8015         compose_list = g_list_append(compose_list, compose);
8016
8017         if (!prefs_common.show_ruler)
8018                 gtk_widget_hide(ruler_hbox);
8019                 
8020         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8021
8022         /* Priority */
8023         compose->priority = PRIORITY_NORMAL;
8024         compose_update_priority_menu_item(compose);
8025
8026         compose_set_out_encoding(compose);
8027         
8028         /* Actions menu */
8029         compose_update_actions_menu(compose);
8030
8031         /* Privacy Systems menu */
8032         compose_update_privacy_systems_menu(compose);
8033
8034         activate_privacy_system(compose, account, TRUE);
8035         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8036         if (batch) {
8037                 gtk_widget_realize(window);
8038         } else {
8039                 gtk_widget_show(window);
8040         }
8041         
8042         return compose;
8043 }
8044
8045 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8046 {
8047         GList *accounts;
8048         GtkWidget *hbox;
8049         GtkWidget *optmenu;
8050         GtkWidget *optmenubox;
8051         GtkListStore *menu;
8052         GtkTreeIter iter;
8053         GtkWidget *from_name = NULL;
8054
8055         gint num = 0, def_menu = 0;
8056         
8057         accounts = account_get_list();
8058         cm_return_val_if_fail(accounts != NULL, NULL);
8059
8060         optmenubox = gtk_event_box_new();
8061         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8062         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8063
8064         hbox = gtk_hbox_new(FALSE, 6);
8065         from_name = gtk_entry_new();
8066         
8067         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8068                          G_CALLBACK(compose_grab_focus_cb), compose);
8069
8070         for (; accounts != NULL; accounts = accounts->next, num++) {
8071                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8072                 gchar *name, *from = NULL;
8073
8074                 if (ac == compose->account) def_menu = num;
8075
8076                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
8077                                        ac->account_name);
8078                 
8079                 if (ac == compose->account) {
8080                         if (ac->name && *ac->name) {
8081                                 gchar *buf;
8082                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8083                                 from = g_strdup_printf("%s <%s>",
8084                                                        buf, ac->address);
8085                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8086                         } else {
8087                                 from = g_strdup_printf("%s",
8088                                                        ac->address);
8089                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8090                         }
8091                 }
8092                 COMBOBOX_ADD(menu, name, ac->account_id);
8093                 g_free(name);
8094                 g_free(from);
8095         }
8096
8097         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8098
8099         g_signal_connect(G_OBJECT(optmenu), "changed",
8100                         G_CALLBACK(account_activated),
8101                         compose);
8102         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8103                          G_CALLBACK(compose_entry_popup_extend),
8104                          NULL);
8105
8106         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8107         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8108
8109         /* Putting only the GtkEntry into focus chain of parent hbox causes
8110          * the account selector combobox next to it to be unreachable when
8111          * navigating widgets in GtkTable with up/down arrow keys.
8112          * Note: gtk_widget_set_can_focus() was not enough. */
8113         GList *l = NULL;
8114         l = g_list_prepend(l, from_name);
8115         gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8116         g_list_free(l);
8117         
8118         CLAWS_SET_TIP(optmenubox,
8119                 _("Account to use for this email"));
8120         CLAWS_SET_TIP(from_name,
8121                 _("Sender address to be used"));
8122
8123         compose->account_combo = optmenu;
8124         compose->from_name = from_name;
8125         
8126         return hbox;
8127 }
8128
8129 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8130 {
8131         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8132         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8133         Compose *compose = (Compose *) data;
8134         if (active) {
8135                 compose->priority = value;
8136         }
8137 }
8138
8139 static void compose_reply_change_mode(Compose *compose,
8140                                     ComposeMode action)
8141 {
8142         gboolean was_modified = compose->modified;
8143
8144         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8145         
8146         cm_return_if_fail(compose->replyinfo != NULL);
8147         
8148         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8149                 ml = TRUE;
8150         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8151                 followup = TRUE;
8152         if (action == COMPOSE_REPLY_TO_ALL)
8153                 all = TRUE;
8154         if (action == COMPOSE_REPLY_TO_SENDER)
8155                 sender = TRUE;
8156         if (action == COMPOSE_REPLY_TO_LIST)
8157                 ml = TRUE;
8158
8159         compose_remove_header_entries(compose);
8160         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8161         if (compose->account->set_autocc && compose->account->auto_cc)
8162                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8163
8164         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8165                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8166         
8167         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8168                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8169         compose_show_first_last_header(compose, TRUE);
8170         compose->modified = was_modified;
8171         compose_set_title(compose);
8172 }
8173
8174 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8175 {
8176         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8177         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8178         Compose *compose = (Compose *) data;
8179         
8180         if (active)
8181                 compose_reply_change_mode(compose, value);
8182 }
8183
8184 static void compose_update_priority_menu_item(Compose * compose)
8185 {
8186         GtkWidget *menuitem = NULL;
8187         switch (compose->priority) {
8188                 case PRIORITY_HIGHEST:
8189                         menuitem = gtk_ui_manager_get_widget
8190                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8191                         break;
8192                 case PRIORITY_HIGH:
8193                         menuitem = gtk_ui_manager_get_widget
8194                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8195                         break;
8196                 case PRIORITY_NORMAL:
8197                         menuitem = gtk_ui_manager_get_widget
8198                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8199                         break;
8200                 case PRIORITY_LOW:
8201                         menuitem = gtk_ui_manager_get_widget
8202                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8203                         break;
8204                 case PRIORITY_LOWEST:
8205                         menuitem = gtk_ui_manager_get_widget
8206                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8207                         break;
8208         }
8209         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8210 }       
8211
8212 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8213 {
8214         Compose *compose = (Compose *) data;
8215         gchar *systemid;
8216         gboolean can_sign = FALSE, can_encrypt = FALSE;
8217
8218         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8219
8220         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8221                 return;
8222
8223         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8224         g_free(compose->privacy_system);
8225         compose->privacy_system = NULL;
8226         if (systemid != NULL) {
8227                 compose->privacy_system = g_strdup(systemid);
8228
8229                 can_sign = privacy_system_can_sign(systemid);
8230                 can_encrypt = privacy_system_can_encrypt(systemid);
8231         }
8232
8233         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8234
8235         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8236         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8237 }
8238
8239 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8240 {
8241         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8242         GtkWidget *menuitem = NULL;
8243         GList *children, *amenu;
8244         gboolean can_sign = FALSE, can_encrypt = FALSE;
8245         gboolean found = FALSE;
8246
8247         if (compose->privacy_system != NULL) {
8248                 gchar *systemid;
8249                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8250                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8251                 cm_return_if_fail(menuitem != NULL);
8252
8253                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8254                 amenu = children;
8255                 menuitem = NULL;
8256                 while (amenu != NULL) {
8257                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8258                         if (systemid != NULL) {
8259                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8260                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8261                                         menuitem = GTK_WIDGET(amenu->data);
8262
8263                                         can_sign = privacy_system_can_sign(systemid);
8264                                         can_encrypt = privacy_system_can_encrypt(systemid);
8265                                         found = TRUE;
8266                                         break;
8267                                 } 
8268                         } else if (strlen(compose->privacy_system) == 0 && 
8269                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8270                                         menuitem = GTK_WIDGET(amenu->data);
8271
8272                                         can_sign = FALSE;
8273                                         can_encrypt = FALSE;
8274                                         found = TRUE;
8275                                         break;
8276                         }
8277
8278                         amenu = amenu->next;
8279                 }
8280                 g_list_free(children);
8281                 if (menuitem != NULL)
8282                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8283                 
8284                 if (warn && !found && strlen(compose->privacy_system)) {
8285                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8286                                   "will not be able to sign or encrypt this message."),
8287                                   compose->privacy_system);
8288                 }
8289         } 
8290
8291         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8292         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8293 }       
8294  
8295 static void compose_set_out_encoding(Compose *compose)
8296 {
8297         CharSet out_encoding;
8298         const gchar *branch = NULL;
8299         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8300
8301         switch(out_encoding) {
8302                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8303                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8304                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8305                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8306                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8307                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8308                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8309                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8310                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8311                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8312                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8313                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8314                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8315                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8316                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8317                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8318                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8319                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8320                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8321                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8322                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8323                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8324                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8325                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8326                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8327                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8328                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8329                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8330                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8331                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8332                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8333                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8334                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8335         }
8336         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8337 }
8338
8339 static void compose_set_template_menu(Compose *compose)
8340 {
8341         GSList *tmpl_list, *cur;
8342         GtkWidget *menu;
8343         GtkWidget *item;
8344
8345         tmpl_list = template_get_config();
8346
8347         menu = gtk_menu_new();
8348
8349         gtk_menu_set_accel_group (GTK_MENU (menu), 
8350                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8351         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8352                 Template *tmpl = (Template *)cur->data;
8353                 gchar *accel_path = NULL;
8354                 item = gtk_menu_item_new_with_label(tmpl->name);
8355                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8356                 g_signal_connect(G_OBJECT(item), "activate",
8357                                  G_CALLBACK(compose_template_activate_cb),
8358                                  compose);
8359                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8360                 gtk_widget_show(item);
8361                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8362                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8363                 g_free(accel_path);
8364         }
8365
8366         gtk_widget_show(menu);
8367         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8368 }
8369
8370 void compose_update_actions_menu(Compose *compose)
8371 {
8372         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8373 }
8374
8375 static void compose_update_privacy_systems_menu(Compose *compose)
8376 {
8377         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8378         GSList *systems, *cur;
8379         GtkWidget *widget;
8380         GtkWidget *system_none;
8381         GSList *group;
8382         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8383         GtkWidget *privacy_menu = gtk_menu_new();
8384
8385         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8386         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8387
8388         g_signal_connect(G_OBJECT(system_none), "activate",
8389                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8390
8391         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8392         gtk_widget_show(system_none);
8393
8394         systems = privacy_get_system_ids();
8395         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8396                 gchar *systemid = cur->data;
8397
8398                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8399                 widget = gtk_radio_menu_item_new_with_label(group,
8400                         privacy_system_get_name(systemid));
8401                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8402                                        g_strdup(systemid), g_free);
8403                 g_signal_connect(G_OBJECT(widget), "activate",
8404                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8405
8406                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8407                 gtk_widget_show(widget);
8408                 g_free(systemid);
8409         }
8410         g_slist_free(systems);
8411         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8412         gtk_widget_show_all(privacy_menu);
8413         gtk_widget_show_all(privacy_menuitem);
8414 }
8415
8416 void compose_reflect_prefs_all(void)
8417 {
8418         GList *cur;
8419         Compose *compose;
8420
8421         for (cur = compose_list; cur != NULL; cur = cur->next) {
8422                 compose = (Compose *)cur->data;
8423                 compose_set_template_menu(compose);
8424         }
8425 }
8426
8427 void compose_reflect_prefs_pixmap_theme(void)
8428 {
8429         GList *cur;
8430         Compose *compose;
8431
8432         for (cur = compose_list; cur != NULL; cur = cur->next) {
8433                 compose = (Compose *)cur->data;
8434                 toolbar_update(TOOLBAR_COMPOSE, compose);
8435         }
8436 }
8437
8438 static const gchar *compose_quote_char_from_context(Compose *compose)
8439 {
8440         const gchar *qmark = NULL;
8441
8442         cm_return_val_if_fail(compose != NULL, NULL);
8443
8444         switch (compose->mode) {
8445                 /* use forward-specific quote char */
8446                 case COMPOSE_FORWARD:
8447                 case COMPOSE_FORWARD_AS_ATTACH:
8448                 case COMPOSE_FORWARD_INLINE:
8449                         if (compose->folder && compose->folder->prefs &&
8450                                         compose->folder->prefs->forward_with_format)
8451                                 qmark = compose->folder->prefs->forward_quotemark;
8452                         else if (compose->account->forward_with_format)
8453                                 qmark = compose->account->forward_quotemark;
8454                         else
8455                                 qmark = prefs_common.fw_quotemark;
8456                         break;
8457
8458                 /* use reply-specific quote char in all other modes */
8459                 default:
8460                         if (compose->folder && compose->folder->prefs &&
8461                                         compose->folder->prefs->reply_with_format)
8462                                 qmark = compose->folder->prefs->reply_quotemark;
8463                         else if (compose->account->reply_with_format)
8464                                 qmark = compose->account->reply_quotemark;
8465                         else
8466                                 qmark = prefs_common.quotemark;
8467                         break;
8468         }
8469
8470         if (qmark == NULL || *qmark == '\0')
8471                 qmark = "> ";
8472
8473         return qmark;
8474 }
8475
8476 static void compose_template_apply(Compose *compose, Template *tmpl,
8477                                    gboolean replace)
8478 {
8479         GtkTextView *text;
8480         GtkTextBuffer *buffer;
8481         GtkTextMark *mark;
8482         GtkTextIter iter;
8483         const gchar *qmark;
8484         gchar *parsed_str = NULL;
8485         gint cursor_pos = 0;
8486         const gchar *err_msg = _("The body of the template has an error at line %d.");
8487         if (!tmpl) return;
8488
8489         /* process the body */
8490
8491         text = GTK_TEXT_VIEW(compose->text);
8492         buffer = gtk_text_view_get_buffer(text);
8493
8494         if (tmpl->value) {
8495                 qmark = compose_quote_char_from_context(compose);
8496
8497                 if (compose->replyinfo != NULL) {
8498
8499                         if (replace)
8500                                 gtk_text_buffer_set_text(buffer, "", -1);
8501                         mark = gtk_text_buffer_get_insert(buffer);
8502                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8503
8504                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8505                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8506
8507                 } else if (compose->fwdinfo != NULL) {
8508
8509                         if (replace)
8510                                 gtk_text_buffer_set_text(buffer, "", -1);
8511                         mark = gtk_text_buffer_get_insert(buffer);
8512                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8513
8514                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8515                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8516
8517                 } else {
8518                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8519
8520                         GtkTextIter start, end;
8521                         gchar *tmp = NULL;
8522
8523                         gtk_text_buffer_get_start_iter(buffer, &start);
8524                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8525                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8526
8527                         /* clear the buffer now */
8528                         if (replace)
8529                                 gtk_text_buffer_set_text(buffer, "", -1);
8530
8531                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8532                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8533                         procmsg_msginfo_free( dummyinfo );
8534
8535                         g_free( tmp );
8536                 } 
8537         } else {
8538                 if (replace)
8539                         gtk_text_buffer_set_text(buffer, "", -1);
8540                 mark = gtk_text_buffer_get_insert(buffer);
8541                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8542         }       
8543
8544         if (replace && parsed_str && compose->account->auto_sig)
8545                 compose_insert_sig(compose, FALSE);
8546
8547         if (replace && parsed_str) {
8548                 gtk_text_buffer_get_start_iter(buffer, &iter);
8549                 gtk_text_buffer_place_cursor(buffer, &iter);
8550         }
8551         
8552         if (parsed_str) {
8553                 cursor_pos = quote_fmt_get_cursor_pos();
8554                 compose->set_cursor_pos = cursor_pos;
8555                 if (cursor_pos == -1)
8556                         cursor_pos = 0;
8557                 gtk_text_buffer_get_start_iter(buffer, &iter);
8558                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8559                 gtk_text_buffer_place_cursor(buffer, &iter);
8560         }
8561
8562         /* process the other fields */
8563
8564         compose_template_apply_fields(compose, tmpl);
8565         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8566         quote_fmt_reset_vartable();
8567         compose_changed_cb(NULL, compose);
8568
8569 #ifdef USE_ENCHANT
8570         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8571                 gtkaspell_highlight_all(compose->gtkaspell);
8572 #endif
8573 }
8574
8575 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8576 {
8577         MsgInfo* dummyinfo = NULL;
8578         MsgInfo *msginfo = NULL;
8579         gchar *buf = NULL;
8580
8581         if (compose->replyinfo != NULL)
8582                 msginfo = compose->replyinfo;
8583         else if (compose->fwdinfo != NULL)
8584                 msginfo = compose->fwdinfo;
8585         else {
8586                 dummyinfo = compose_msginfo_new_from_compose(compose);
8587                 msginfo = dummyinfo;
8588         }
8589
8590         if (tmpl->from && *tmpl->from != '\0') {
8591 #ifdef USE_ENCHANT
8592                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8593                                 compose->gtkaspell);
8594 #else
8595                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8596 #endif
8597                 quote_fmt_scan_string(tmpl->from);
8598                 quote_fmt_parse();
8599
8600                 buf = quote_fmt_get_buffer();
8601                 if (buf == NULL) {
8602                         alertpanel_error(_("Template From format error."));
8603                 } else {
8604                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8605                 }
8606         }
8607
8608         if (tmpl->to && *tmpl->to != '\0') {
8609 #ifdef USE_ENCHANT
8610                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8611                                 compose->gtkaspell);
8612 #else
8613                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8614 #endif
8615                 quote_fmt_scan_string(tmpl->to);
8616                 quote_fmt_parse();
8617
8618                 buf = quote_fmt_get_buffer();
8619                 if (buf == NULL) {
8620                         alertpanel_error(_("Template To format error."));
8621                 } else {
8622                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8623                 }
8624         }
8625
8626         if (tmpl->cc && *tmpl->cc != '\0') {
8627 #ifdef USE_ENCHANT
8628                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8629                                 compose->gtkaspell);
8630 #else
8631                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8632 #endif
8633                 quote_fmt_scan_string(tmpl->cc);
8634                 quote_fmt_parse();
8635
8636                 buf = quote_fmt_get_buffer();
8637                 if (buf == NULL) {
8638                         alertpanel_error(_("Template Cc format error."));
8639                 } else {
8640                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8641                 }
8642         }
8643
8644         if (tmpl->bcc && *tmpl->bcc != '\0') {
8645 #ifdef USE_ENCHANT
8646                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8647                                 compose->gtkaspell);
8648 #else
8649                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8650 #endif
8651                 quote_fmt_scan_string(tmpl->bcc);
8652                 quote_fmt_parse();
8653
8654                 buf = quote_fmt_get_buffer();
8655                 if (buf == NULL) {
8656                         alertpanel_error(_("Template Bcc format error."));
8657                 } else {
8658                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8659                 }
8660         }
8661
8662         /* process the subject */
8663         if (tmpl->subject && *tmpl->subject != '\0') {
8664 #ifdef USE_ENCHANT
8665                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8666                                 compose->gtkaspell);
8667 #else
8668                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8669 #endif
8670                 quote_fmt_scan_string(tmpl->subject);
8671                 quote_fmt_parse();
8672
8673                 buf = quote_fmt_get_buffer();
8674                 if (buf == NULL) {
8675                         alertpanel_error(_("Template subject format error."));
8676                 } else {
8677                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8678                 }
8679         }
8680
8681         procmsg_msginfo_free( dummyinfo );
8682 }
8683
8684 static void compose_destroy(Compose *compose)
8685 {
8686         GtkAllocation allocation;
8687         GtkTextBuffer *buffer;
8688         GtkClipboard *clipboard;
8689
8690         compose_list = g_list_remove(compose_list, compose);
8691
8692         if (compose->updating) {
8693                 debug_print("danger, not destroying anything now\n");
8694                 compose->deferred_destroy = TRUE;
8695                 return;
8696         }
8697
8698         /* NOTE: address_completion_end() does nothing with the window
8699          * however this may change. */
8700         address_completion_end(compose->window);
8701
8702         slist_free_strings_full(compose->to_list);
8703         slist_free_strings_full(compose->newsgroup_list);
8704         slist_free_strings_full(compose->header_list);
8705
8706         slist_free_strings_full(extra_headers);
8707         extra_headers = NULL;
8708
8709         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8710
8711         g_hash_table_destroy(compose->email_hashtable);
8712
8713         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8714                         compose->folder_update_callback_id);
8715
8716         procmsg_msginfo_free(compose->targetinfo);
8717         procmsg_msginfo_free(compose->replyinfo);
8718         procmsg_msginfo_free(compose->fwdinfo);
8719
8720         g_free(compose->replyto);
8721         g_free(compose->cc);
8722         g_free(compose->bcc);
8723         g_free(compose->newsgroups);
8724         g_free(compose->followup_to);
8725
8726         g_free(compose->ml_post);
8727
8728         g_free(compose->inreplyto);
8729         g_free(compose->references);
8730         g_free(compose->msgid);
8731         g_free(compose->boundary);
8732
8733         g_free(compose->redirect_filename);
8734         if (compose->undostruct)
8735                 undo_destroy(compose->undostruct);
8736
8737         g_free(compose->sig_str);
8738
8739         g_free(compose->exteditor_file);
8740
8741         g_free(compose->orig_charset);
8742
8743         g_free(compose->privacy_system);
8744
8745 #ifndef USE_NEW_ADDRBOOK
8746         if (addressbook_get_target_compose() == compose)
8747                 addressbook_set_target_compose(NULL);
8748 #endif
8749 #if USE_ENCHANT
8750         if (compose->gtkaspell) {
8751                 gtkaspell_delete(compose->gtkaspell);
8752                 compose->gtkaspell = NULL;
8753         }
8754 #endif
8755
8756         if (!compose->batch) {
8757                 gtk_widget_get_allocation(compose->window, &allocation);
8758                 prefs_common.compose_width = allocation.width;
8759                 prefs_common.compose_height = allocation.height;
8760         }
8761
8762         if (!gtk_widget_get_parent(compose->paned))
8763                 gtk_widget_destroy(compose->paned);
8764         gtk_widget_destroy(compose->popupmenu);
8765
8766         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8767         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8768         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8769
8770         gtk_widget_destroy(compose->window);
8771         toolbar_destroy(compose->toolbar);
8772         g_free(compose->toolbar);
8773         cm_mutex_free(compose->mutex);
8774         g_free(compose);
8775 }
8776
8777 static void compose_attach_info_free(AttachInfo *ainfo)
8778 {
8779         g_free(ainfo->file);
8780         g_free(ainfo->content_type);
8781         g_free(ainfo->name);
8782         g_free(ainfo->charset);
8783         g_free(ainfo);
8784 }
8785
8786 static void compose_attach_update_label(Compose *compose)
8787 {
8788         GtkTreeIter iter;
8789         gint i = 1;
8790         gchar *text;
8791         GtkTreeModel *model;
8792         
8793         if(compose == NULL)
8794                 return;
8795                 
8796         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8797         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8798                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8799                 return;
8800         }
8801         
8802         while(gtk_tree_model_iter_next(model, &iter))
8803                 i++;
8804         
8805         text = g_strdup_printf("(%d)", i);
8806         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8807         g_free(text);
8808 }
8809
8810 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8811 {
8812         Compose *compose = (Compose *)data;
8813         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8814         GtkTreeSelection *selection;
8815         GList *sel, *cur;
8816         GtkTreeModel *model;
8817
8818         selection = gtk_tree_view_get_selection(tree_view);
8819         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8820
8821         if (!sel) 
8822                 return;
8823
8824         for (cur = sel; cur != NULL; cur = cur->next) {
8825                 GtkTreePath *path = cur->data;
8826                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8827                                                 (model, cur->data);
8828                 cur->data = ref;
8829                 gtk_tree_path_free(path);
8830         }
8831
8832         for (cur = sel; cur != NULL; cur = cur->next) {
8833                 GtkTreeRowReference *ref = cur->data;
8834                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8835                 GtkTreeIter iter;
8836
8837                 if (gtk_tree_model_get_iter(model, &iter, path))
8838                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8839                 
8840                 gtk_tree_path_free(path);
8841                 gtk_tree_row_reference_free(ref);
8842         }
8843
8844         g_list_free(sel);
8845         compose_attach_update_label(compose);
8846 }
8847
8848 static struct _AttachProperty
8849 {
8850         GtkWidget *window;
8851         GtkWidget *mimetype_entry;
8852         GtkWidget *encoding_optmenu;
8853         GtkWidget *path_entry;
8854         GtkWidget *filename_entry;
8855         GtkWidget *ok_btn;
8856         GtkWidget *cancel_btn;
8857 } attach_prop;
8858
8859 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8860 {       
8861         gtk_tree_path_free((GtkTreePath *)ptr);
8862 }
8863
8864 static void compose_attach_property(GtkAction *action, gpointer data)
8865 {
8866         Compose *compose = (Compose *)data;
8867         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8868         AttachInfo *ainfo;
8869         GtkComboBox *optmenu;
8870         GtkTreeSelection *selection;
8871         GList *sel;
8872         GtkTreeModel *model;
8873         GtkTreeIter iter;
8874         GtkTreePath *path;
8875         static gboolean cancelled;
8876
8877         /* only if one selected */
8878         selection = gtk_tree_view_get_selection(tree_view);
8879         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8880                 return;
8881
8882         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8883         if (!sel)
8884                 return;
8885
8886         path = (GtkTreePath *) sel->data;
8887         gtk_tree_model_get_iter(model, &iter, path);
8888         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8889         
8890         if (!ainfo) {
8891                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8892                 g_list_free(sel);
8893                 return;
8894         }               
8895         g_list_free(sel);
8896
8897         if (!attach_prop.window)
8898                 compose_attach_property_create(&cancelled);
8899         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8900         gtk_widget_grab_focus(attach_prop.ok_btn);
8901         gtk_widget_show(attach_prop.window);
8902         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8903                         GTK_WINDOW(compose->window));
8904
8905         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8906         if (ainfo->encoding == ENC_UNKNOWN)
8907                 combobox_select_by_data(optmenu, ENC_BASE64);
8908         else
8909                 combobox_select_by_data(optmenu, ainfo->encoding);
8910
8911         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8912                            ainfo->content_type ? ainfo->content_type : "");
8913         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8914                            ainfo->file ? ainfo->file : "");
8915         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8916                            ainfo->name ? ainfo->name : "");
8917
8918         for (;;) {
8919                 const gchar *entry_text;
8920                 gchar *text;
8921                 gchar *cnttype = NULL;
8922                 gchar *file = NULL;
8923                 off_t size = 0;
8924
8925                 cancelled = FALSE;
8926                 gtk_main();
8927
8928                 gtk_widget_hide(attach_prop.window);
8929                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8930                 
8931                 if (cancelled)
8932                         break;
8933
8934                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8935                 if (*entry_text != '\0') {
8936                         gchar *p;
8937
8938                         text = g_strstrip(g_strdup(entry_text));
8939                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8940                                 cnttype = g_strdup(text);
8941                                 g_free(text);
8942                         } else {
8943                                 alertpanel_error(_("Invalid MIME type."));
8944                                 g_free(text);
8945                                 continue;
8946                         }
8947                 }
8948
8949                 ainfo->encoding = combobox_get_active_data(optmenu);
8950
8951                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8952                 if (*entry_text != '\0') {
8953                         if (is_file_exist(entry_text) &&
8954                             (size = get_file_size(entry_text)) > 0)
8955                                 file = g_strdup(entry_text);
8956                         else {
8957                                 alertpanel_error
8958                                         (_("File doesn't exist or is empty."));
8959                                 g_free(cnttype);
8960                                 continue;
8961                         }
8962                 }
8963
8964                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8965                 if (*entry_text != '\0') {
8966                         g_free(ainfo->name);
8967                         ainfo->name = g_strdup(entry_text);
8968                 }
8969
8970                 if (cnttype) {
8971                         g_free(ainfo->content_type);
8972                         ainfo->content_type = cnttype;
8973                 }
8974                 if (file) {
8975                         g_free(ainfo->file);
8976                         ainfo->file = file;
8977                 }
8978                 if (size)
8979                         ainfo->size = (goffset)size;
8980
8981                 /* update tree store */
8982                 text = to_human_readable(ainfo->size);
8983                 gtk_tree_model_get_iter(model, &iter, path);
8984                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8985                                    COL_MIMETYPE, ainfo->content_type,
8986                                    COL_SIZE, text,
8987                                    COL_NAME, ainfo->name,
8988                                    COL_CHARSET, ainfo->charset,
8989                                    -1);
8990                 
8991                 break;
8992         }
8993
8994         gtk_tree_path_free(path);
8995 }
8996
8997 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8998 { \
8999         label = gtk_label_new(str); \
9000         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9001                          GTK_FILL, 0, 0, 0); \
9002         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9003  \
9004         entry = gtk_entry_new(); \
9005         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9006                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9007 }
9008
9009 static void compose_attach_property_create(gboolean *cancelled)
9010 {
9011         GtkWidget *window;
9012         GtkWidget *vbox;
9013         GtkWidget *table;
9014         GtkWidget *label;
9015         GtkWidget *mimetype_entry;
9016         GtkWidget *hbox;
9017         GtkWidget *optmenu;
9018         GtkListStore *optmenu_menu;
9019         GtkWidget *path_entry;
9020         GtkWidget *filename_entry;
9021         GtkWidget *hbbox;
9022         GtkWidget *ok_btn;
9023         GtkWidget *cancel_btn;
9024         GList     *mime_type_list, *strlist;
9025         GtkTreeIter iter;
9026
9027         debug_print("Creating attach_property window...\n");
9028
9029         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9030         gtk_widget_set_size_request(window, 480, -1);
9031         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9032         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9033         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9034         g_signal_connect(G_OBJECT(window), "delete_event",
9035                          G_CALLBACK(attach_property_delete_event),
9036                          cancelled);
9037         g_signal_connect(G_OBJECT(window), "key_press_event",
9038                          G_CALLBACK(attach_property_key_pressed),
9039                          cancelled);
9040
9041         vbox = gtk_vbox_new(FALSE, 8);
9042         gtk_container_add(GTK_CONTAINER(window), vbox);
9043
9044         table = gtk_table_new(4, 2, FALSE);
9045         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9046         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9047         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9048
9049         label = gtk_label_new(_("MIME type")); 
9050         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9051                          GTK_FILL, 0, 0, 0); 
9052         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9053 #if !GTK_CHECK_VERSION(2, 24, 0)
9054         mimetype_entry = gtk_combo_box_entry_new_text(); 
9055 #else
9056         mimetype_entry = gtk_combo_box_text_new_with_entry();
9057 #endif
9058         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9059                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9060                          
9061         /* stuff with list */
9062         mime_type_list = procmime_get_mime_type_list();
9063         strlist = NULL;
9064         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9065                 MimeType *type = (MimeType *) mime_type_list->data;
9066                 gchar *tmp;
9067
9068                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9069
9070                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9071                         g_free(tmp);
9072                 else
9073                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9074                                         (GCompareFunc)strcmp2);
9075         }
9076
9077         for (mime_type_list = strlist; mime_type_list != NULL; 
9078                 mime_type_list = mime_type_list->next) {
9079 #if !GTK_CHECK_VERSION(2, 24, 0)
9080                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9081 #else
9082                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9083 #endif
9084                 g_free(mime_type_list->data);
9085         }
9086         g_list_free(strlist);
9087         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9088         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9089
9090         label = gtk_label_new(_("Encoding"));
9091         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9092                          GTK_FILL, 0, 0, 0);
9093         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9094
9095         hbox = gtk_hbox_new(FALSE, 0);
9096         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9097                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9098
9099         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9100         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9101
9102         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9103         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9104         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9105         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9106         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9107
9108         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9109
9110         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9111         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9112
9113         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9114                                       &ok_btn, GTK_STOCK_OK,
9115                                       NULL, NULL);
9116         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9117         gtk_widget_grab_default(ok_btn);
9118
9119         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9120                          G_CALLBACK(attach_property_ok),
9121                          cancelled);
9122         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9123                          G_CALLBACK(attach_property_cancel),
9124                          cancelled);
9125
9126         gtk_widget_show_all(vbox);
9127
9128         attach_prop.window           = window;
9129         attach_prop.mimetype_entry   = mimetype_entry;
9130         attach_prop.encoding_optmenu = optmenu;
9131         attach_prop.path_entry       = path_entry;
9132         attach_prop.filename_entry   = filename_entry;
9133         attach_prop.ok_btn           = ok_btn;
9134         attach_prop.cancel_btn       = cancel_btn;
9135 }
9136
9137 #undef SET_LABEL_AND_ENTRY
9138
9139 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9140 {
9141         *cancelled = FALSE;
9142         gtk_main_quit();
9143 }
9144
9145 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9146 {
9147         *cancelled = TRUE;
9148         gtk_main_quit();
9149 }
9150
9151 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9152                                          gboolean *cancelled)
9153 {
9154         *cancelled = TRUE;
9155         gtk_main_quit();
9156
9157         return TRUE;
9158 }
9159
9160 static gboolean attach_property_key_pressed(GtkWidget *widget,
9161                                             GdkEventKey *event,
9162                                             gboolean *cancelled)
9163 {
9164         if (event && event->keyval == GDK_KEY_Escape) {
9165                 *cancelled = TRUE;
9166                 gtk_main_quit();
9167         }
9168         if (event && event->keyval == GDK_KEY_Return) {
9169                 *cancelled = FALSE;
9170                 gtk_main_quit();
9171                 return TRUE;
9172         }
9173         return FALSE;
9174 }
9175
9176 static void compose_exec_ext_editor(Compose *compose)
9177 {
9178 #ifdef G_OS_UNIX
9179         gchar *tmp;
9180         pid_t pid;
9181         gint pipe_fds[2];
9182
9183         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9184                               G_DIR_SEPARATOR, compose);
9185
9186         if (pipe(pipe_fds) < 0) {
9187                 perror("pipe");
9188                 g_free(tmp);
9189                 return;
9190         }
9191
9192         if ((pid = fork()) < 0) {
9193                 perror("fork");
9194                 g_free(tmp);
9195                 return;
9196         }
9197
9198         if (pid != 0) {
9199                 /* close the write side of the pipe */
9200                 close(pipe_fds[1]);
9201
9202                 compose->exteditor_file    = g_strdup(tmp);
9203                 compose->exteditor_pid     = pid;
9204
9205                 compose_set_ext_editor_sensitive(compose, FALSE);
9206
9207 #ifndef G_OS_WIN32
9208                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9209 #else
9210                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9211 #endif
9212                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9213                                                         G_IO_IN,
9214                                                         compose_input_cb,
9215                                                         compose);
9216         } else {        /* process-monitoring process */
9217                 pid_t pid_ed;
9218
9219                 if (setpgid(0, 0))
9220                         perror("setpgid");
9221
9222                 /* close the read side of the pipe */
9223                 close(pipe_fds[0]);
9224
9225                 if (compose_write_body_to_file(compose, tmp) < 0) {
9226                         fd_write_all(pipe_fds[1], "2\n", 2);
9227                         _exit(1);
9228                 }
9229
9230                 pid_ed = compose_exec_ext_editor_real(tmp);
9231                 if (pid_ed < 0) {
9232                         fd_write_all(pipe_fds[1], "1\n", 2);
9233                         _exit(1);
9234                 }
9235
9236                 /* wait until editor is terminated */
9237                 waitpid(pid_ed, NULL, 0);
9238
9239                 fd_write_all(pipe_fds[1], "0\n", 2);
9240
9241                 close(pipe_fds[1]);
9242                 _exit(0);
9243         }
9244
9245         g_free(tmp);
9246 #endif /* G_OS_UNIX */
9247 }
9248
9249 #ifdef G_OS_UNIX
9250 static gint compose_exec_ext_editor_real(const gchar *file)
9251 {
9252         gchar buf[1024];
9253         gchar *p;
9254         gchar **cmdline;
9255         pid_t pid;
9256
9257         cm_return_val_if_fail(file != NULL, -1);
9258
9259         if ((pid = fork()) < 0) {
9260                 perror("fork");
9261                 return -1;
9262         }
9263
9264         if (pid != 0) return pid;
9265
9266         /* grandchild process */
9267
9268         if (setpgid(0, getppid()))
9269                 perror("setpgid");
9270
9271         if (prefs_common_get_ext_editor_cmd() &&
9272             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9273             *(p + 1) == 's' && !strchr(p + 2, '%')) {
9274                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9275         } else {
9276                 if (prefs_common_get_ext_editor_cmd())
9277                         g_warning("External editor command-line is invalid: '%s'\n",
9278                                   prefs_common_get_ext_editor_cmd());
9279                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9280         }
9281
9282         cmdline = strsplit_with_quote(buf, " ", 1024);
9283         execvp(cmdline[0], cmdline);
9284
9285         perror("execvp");
9286         g_strfreev(cmdline);
9287
9288         _exit(1);
9289 }
9290
9291 static gboolean compose_ext_editor_kill(Compose *compose)
9292 {
9293         pid_t pgid = compose->exteditor_pid * -1;
9294         gint ret;
9295
9296         ret = kill(pgid, 0);
9297
9298         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9299                 AlertValue val;
9300                 gchar *msg;
9301
9302                 msg = g_strdup_printf
9303                         (_("The external editor is still working.\n"
9304                            "Force terminating the process?\n"
9305                            "process group id: %d"), -pgid);
9306                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9307                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9308                         
9309                 g_free(msg);
9310
9311                 if (val == G_ALERTALTERNATE) {
9312                         g_source_remove(compose->exteditor_tag);
9313                         g_io_channel_shutdown(compose->exteditor_ch,
9314                                               FALSE, NULL);
9315                         g_io_channel_unref(compose->exteditor_ch);
9316
9317                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9318                         waitpid(compose->exteditor_pid, NULL, 0);
9319
9320                         g_warning("Terminated process group id: %d", -pgid);
9321                         g_warning("Temporary file: %s",
9322                                   compose->exteditor_file);
9323
9324                         compose_set_ext_editor_sensitive(compose, TRUE);
9325
9326                         g_free(compose->exteditor_file);
9327                         compose->exteditor_file    = NULL;
9328                         compose->exteditor_pid     = -1;
9329                         compose->exteditor_ch      = NULL;
9330                         compose->exteditor_tag     = -1;
9331                 } else
9332                         return FALSE;
9333         }
9334
9335         return TRUE;
9336 }
9337
9338 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9339                                  gpointer data)
9340 {
9341         gchar buf[3] = "3";
9342         Compose *compose = (Compose *)data;
9343         gsize bytes_read;
9344
9345         debug_print("Compose: input from monitoring process\n");
9346
9347         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9348
9349         g_io_channel_shutdown(source, FALSE, NULL);
9350         g_io_channel_unref(source);
9351
9352         waitpid(compose->exteditor_pid, NULL, 0);
9353
9354         if (buf[0] == '0') {            /* success */
9355                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9356                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9357
9358                 gtk_text_buffer_set_text(buffer, "", -1);
9359                 compose_insert_file(compose, compose->exteditor_file);
9360                 compose_changed_cb(NULL, compose);
9361                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9362
9363                 if (claws_unlink(compose->exteditor_file) < 0)
9364                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9365         } else if (buf[0] == '1') {     /* failed */
9366                 g_warning("Couldn't exec external editor\n");
9367                 if (claws_unlink(compose->exteditor_file) < 0)
9368                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9369         } else if (buf[0] == '2') {
9370                 g_warning("Couldn't write to file\n");
9371         } else if (buf[0] == '3') {
9372                 g_warning("Pipe read failed\n");
9373         }
9374
9375         compose_set_ext_editor_sensitive(compose, TRUE);
9376
9377         g_free(compose->exteditor_file);
9378         compose->exteditor_file    = NULL;
9379         compose->exteditor_pid     = -1;
9380         compose->exteditor_ch      = NULL;
9381         compose->exteditor_tag     = -1;
9382
9383         return FALSE;
9384 }
9385
9386 static void compose_set_ext_editor_sensitive(Compose *compose,
9387                                              gboolean sensitive)
9388 {
9389         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9390         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9391         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9392         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9393         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", sensitive);
9394         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9395         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9396         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9397
9398         gtk_widget_set_sensitive(compose->text,                       sensitive);
9399         if (compose->toolbar->send_btn)
9400                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9401         if (compose->toolbar->sendl_btn)
9402                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9403         if (compose->toolbar->draft_btn)
9404                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9405         if (compose->toolbar->insert_btn)
9406                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9407         if (compose->toolbar->sig_btn)
9408                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9409         if (compose->toolbar->exteditor_btn)
9410                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9411         if (compose->toolbar->linewrap_current_btn)
9412                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9413         if (compose->toolbar->linewrap_all_btn)
9414                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9415 }
9416 #endif /* G_OS_UNIX */
9417
9418 /**
9419  * compose_undo_state_changed:
9420  *
9421  * Change the sensivity of the menuentries undo and redo
9422  **/
9423 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9424                                        gint redo_state, gpointer data)
9425 {
9426         Compose *compose = (Compose *)data;
9427
9428         switch (undo_state) {
9429         case UNDO_STATE_TRUE:
9430                 if (!undostruct->undo_state) {
9431                         undostruct->undo_state = TRUE;
9432                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9433                 }
9434                 break;
9435         case UNDO_STATE_FALSE:
9436                 if (undostruct->undo_state) {
9437                         undostruct->undo_state = FALSE;
9438                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9439                 }
9440                 break;
9441         case UNDO_STATE_UNCHANGED:
9442                 break;
9443         case UNDO_STATE_REFRESH:
9444                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9445                 break;
9446         default:
9447                 g_warning("Undo state not recognized");
9448                 break;
9449         }
9450
9451         switch (redo_state) {
9452         case UNDO_STATE_TRUE:
9453                 if (!undostruct->redo_state) {
9454                         undostruct->redo_state = TRUE;
9455                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9456                 }
9457                 break;
9458         case UNDO_STATE_FALSE:
9459                 if (undostruct->redo_state) {
9460                         undostruct->redo_state = FALSE;
9461                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9462                 }
9463                 break;
9464         case UNDO_STATE_UNCHANGED:
9465                 break;
9466         case UNDO_STATE_REFRESH:
9467                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9468                 break;
9469         default:
9470                 g_warning("Redo state not recognized");
9471                 break;
9472         }
9473 }
9474
9475 /* callback functions */
9476
9477 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9478                                         GtkAllocation *allocation,
9479                                         GtkPaned *paned)
9480 {
9481         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9482 }
9483
9484 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9485  * includes "non-client" (windows-izm) in calculation, so this calculation
9486  * may not be accurate.
9487  */
9488 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9489                                         GtkAllocation *allocation,
9490                                         GtkSHRuler *shruler)
9491 {
9492         if (prefs_common.show_ruler) {
9493                 gint char_width = 0, char_height = 0;
9494                 gint line_width_in_chars;
9495
9496                 gtkut_get_font_size(GTK_WIDGET(widget),
9497                                     &char_width, &char_height);
9498                 line_width_in_chars =
9499                         (allocation->width - allocation->x) / char_width;
9500
9501                 /* got the maximum */
9502                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9503                                     0.0, line_width_in_chars, 0);
9504         }
9505
9506         return TRUE;
9507 }
9508
9509 typedef struct {
9510         gchar                   *header;
9511         gchar                   *entry;
9512         ComposePrefType         type;
9513         gboolean                entry_marked;
9514 } HeaderEntryState;
9515
9516 static void account_activated(GtkComboBox *optmenu, gpointer data)
9517 {
9518         Compose *compose = (Compose *)data;
9519
9520         PrefsAccount *ac;
9521         gchar *folderidentifier;
9522         gint account_id = 0;
9523         GtkTreeModel *menu;
9524         GtkTreeIter iter;
9525         GSList *list, *saved_list = NULL;
9526         HeaderEntryState *state;
9527         GtkRcStyle *style = NULL;
9528 #if !GTK_CHECK_VERSION(3, 0, 0)
9529         static GdkColor yellow;
9530         static gboolean color_set = FALSE;
9531 #else
9532         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9533 #endif
9534
9535         /* Get ID of active account in the combo box */
9536         menu = gtk_combo_box_get_model(optmenu);
9537         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9538         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9539
9540         ac = account_find_from_id(account_id);
9541         cm_return_if_fail(ac != NULL);
9542
9543         if (ac != compose->account) {
9544                 compose_select_account(compose, ac, FALSE);
9545
9546                 for (list = compose->header_list; list; list = list->next) {
9547                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9548                         
9549                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9550                                 compose_destroy_headerentry(compose, hentry);
9551                                 continue;
9552                         }
9553                         
9554                         state = g_malloc0(sizeof(HeaderEntryState));
9555                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9556                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9557                         state->entry = gtk_editable_get_chars(
9558                                         GTK_EDITABLE(hentry->entry), 0, -1);
9559                         state->type = hentry->type;
9560                                 
9561 #if !GTK_CHECK_VERSION(3, 0, 0)
9562                         if (!color_set) {
9563                                 gdk_color_parse("#f5f6be", &yellow);
9564                                 color_set = gdk_colormap_alloc_color(
9565                                                         gdk_colormap_get_system(),
9566                                                         &yellow, FALSE, TRUE);
9567                         }
9568 #endif
9569                                 
9570                         style = gtk_widget_get_modifier_style(hentry->entry);
9571                         state->entry_marked = gdk_color_equal(&yellow,
9572                                                 &style->base[GTK_STATE_NORMAL]);
9573
9574                         saved_list = g_slist_append(saved_list, state);
9575                         compose_destroy_headerentry(compose, hentry);
9576                 }
9577
9578                 compose->header_last = NULL;
9579                 g_slist_free(compose->header_list);
9580                 compose->header_list = NULL;
9581                 compose->header_nextrow = 1;
9582                 compose_create_header_entry(compose);
9583                 
9584                 if (ac->set_autocc && ac->auto_cc)
9585                         compose_entry_append(compose, ac->auto_cc,
9586                                                 COMPOSE_CC, PREF_ACCOUNT);
9587
9588                 if (ac->set_autobcc && ac->auto_bcc) 
9589                         compose_entry_append(compose, ac->auto_bcc,
9590                                                 COMPOSE_BCC, PREF_ACCOUNT);
9591         
9592                 if (ac->set_autoreplyto && ac->auto_replyto)
9593                         compose_entry_append(compose, ac->auto_replyto,
9594                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9595                 
9596                 for (list = saved_list; list; list = list->next) {
9597                         state = (HeaderEntryState *) list->data;
9598                         
9599                         compose_add_header_entry(compose, state->header,
9600                                                 state->entry, state->type);
9601                         if (state->entry_marked)
9602                                 compose_entry_mark_default_to(compose, state->entry);
9603                         
9604                         g_free(state->header);  
9605                         g_free(state->entry);
9606                         g_free(state);
9607                 }
9608                 g_slist_free(saved_list);
9609                 
9610                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9611                                         (ac->protocol == A_NNTP) ? 
9612                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9613         }
9614
9615         /* Set message save folder */
9616         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9617                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9618         }
9619         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9620                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9621                            
9622         compose_set_save_to(compose, NULL);
9623         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9624                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9625                                   (compose->account, F_OUTBOX));
9626                 compose_set_save_to(compose, folderidentifier);
9627                 g_free(folderidentifier);
9628         }
9629 }
9630
9631 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9632                             GtkTreeViewColumn *column, Compose *compose)
9633 {
9634         compose_attach_property(NULL, compose);
9635 }
9636
9637 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9638                                       gpointer data)
9639 {
9640         Compose *compose = (Compose *)data;
9641         GtkTreeSelection *attach_selection;
9642         gint attach_nr_selected;
9643         
9644         if (!event) return FALSE;
9645
9646         if (event->button == 3) {
9647                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9648                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9649                         
9650                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9651                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9652                         
9653                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9654                                NULL, NULL, event->button, event->time);
9655                 return TRUE;                           
9656         }
9657
9658         return FALSE;
9659 }
9660
9661 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9662                                    gpointer data)
9663 {
9664         Compose *compose = (Compose *)data;
9665
9666         if (!event) return FALSE;
9667
9668         switch (event->keyval) {
9669         case GDK_KEY_Delete:
9670                 compose_attach_remove_selected(NULL, compose);
9671                 break;
9672         }
9673         return FALSE;
9674 }
9675
9676 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9677 {
9678         toolbar_comp_set_sensitive(compose, allow);
9679         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9680         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9681 #if USE_ENCHANT
9682         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9683 #endif  
9684         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9685         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9686         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9687         
9688         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9689
9690 }
9691
9692 static void compose_send_cb(GtkAction *action, gpointer data)
9693 {
9694         Compose *compose = (Compose *)data;
9695
9696         if (prefs_common.work_offline && 
9697             !inc_offline_should_override(TRUE,
9698                 _("Claws Mail needs network access in order "
9699                   "to send this email.")))
9700                 return;
9701         
9702         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9703                 g_source_remove(compose->draft_timeout_tag);
9704                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
9705         }
9706
9707         compose_send(compose);
9708 }
9709
9710 static void compose_send_later_cb(GtkAction *action, gpointer data)
9711 {
9712         Compose *compose = (Compose *)data;
9713         gint val;
9714
9715         inc_lock();
9716         compose_allow_user_actions(compose, FALSE);
9717         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9718         compose_allow_user_actions(compose, TRUE);
9719         inc_unlock();
9720
9721         if (!val) {
9722                 compose_close(compose);
9723         } else if (val == -1) {
9724                 alertpanel_error(_("Could not queue message."));
9725         } else if (val == -2) {
9726                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9727         } else if (val == -3) {
9728                 if (privacy_peek_error())
9729                 alertpanel_error(_("Could not queue message for sending:\n\n"
9730                                    "Signature failed: %s"), privacy_get_error());
9731         } else if (val == -4) {
9732                 alertpanel_error(_("Could not queue message for sending:\n\n"
9733                                    "Charset conversion failed."));
9734         } else if (val == -5) {
9735                 alertpanel_error(_("Could not queue message for sending:\n\n"
9736                                    "Couldn't get recipient encryption key."));
9737         } else if (val == -6) {
9738                 /* silent error */
9739         }
9740         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9741 }
9742
9743 #define DRAFTED_AT_EXIT "drafted_at_exit"
9744 static void compose_register_draft(MsgInfo *info)
9745 {
9746         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9747                                       DRAFTED_AT_EXIT, NULL);
9748         FILE *fp = g_fopen(filepath, "ab");
9749         
9750         if (fp) {
9751                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9752                                 info->msgnum);
9753                 fclose(fp);
9754         }
9755                 
9756         g_free(filepath);       
9757 }
9758
9759 gboolean compose_draft (gpointer data, guint action) 
9760 {
9761         Compose *compose = (Compose *)data;
9762         FolderItem *draft;
9763         gchar *tmp;
9764         gchar *sheaders;
9765         gint msgnum;
9766         MsgFlags flag = {0, 0};
9767         static gboolean lock = FALSE;
9768         MsgInfo *newmsginfo;
9769         FILE *fp;
9770         gboolean target_locked = FALSE;
9771         gboolean err = FALSE;
9772
9773         if (lock) return FALSE;
9774
9775         if (compose->sending)
9776                 return TRUE;
9777
9778         draft = account_get_special_folder(compose->account, F_DRAFT);
9779         cm_return_val_if_fail(draft != NULL, FALSE);
9780         
9781         if (!g_mutex_trylock(compose->mutex)) {
9782                 /* we don't want to lock the mutex once it's available,
9783                  * because as the only other part of compose.c locking
9784                  * it is compose_close - which means once unlocked,
9785                  * the compose struct will be freed */
9786                 debug_print("couldn't lock mutex, probably sending\n");
9787                 return FALSE;
9788         }
9789
9790         lock = TRUE;
9791
9792         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9793                               G_DIR_SEPARATOR, compose);
9794         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9795                 FILE_OP_ERROR(tmp, "fopen");
9796                 goto warn_err;
9797         }
9798
9799         /* chmod for security */
9800         if (change_file_mode_rw(fp, tmp) < 0) {
9801                 FILE_OP_ERROR(tmp, "chmod");
9802                 g_warning("can't change file mode\n");
9803         }
9804
9805         /* Save draft infos */
9806         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9807         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9808
9809         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9810                 gchar *savefolderid;
9811
9812                 savefolderid = compose_get_save_to(compose);
9813                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9814                 g_free(savefolderid);
9815         }
9816         if (compose->return_receipt) {
9817                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9818         }
9819         if (compose->privacy_system) {
9820                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9821                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9822                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9823         }
9824
9825         /* Message-ID of message replying to */
9826         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9827                 gchar *folderid = NULL;
9828
9829                 if (compose->replyinfo->folder)
9830                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
9831                 if (folderid == NULL)
9832                         folderid = g_strdup("NULL");
9833
9834                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9835                 g_free(folderid);
9836         }
9837         /* Message-ID of message forwarding to */
9838         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9839                 gchar *folderid = NULL;
9840
9841                 if (compose->fwdinfo->folder)
9842                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9843                 if (folderid == NULL)
9844                         folderid = g_strdup("NULL");
9845
9846                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9847                 g_free(folderid);
9848         }
9849
9850         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9851         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9852
9853         sheaders = compose_get_manual_headers_info(compose);
9854         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9855         g_free(sheaders);
9856
9857         /* end of headers */
9858         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9859
9860         if (err) {
9861                 fclose(fp);
9862                 goto warn_err;
9863         }
9864
9865         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9866                 fclose(fp);
9867                 goto warn_err;
9868         }
9869         if (fclose(fp) == EOF) {
9870                 goto warn_err;
9871         }
9872         
9873         flag.perm_flags = MSG_NEW|MSG_UNREAD;
9874         if (compose->targetinfo) {
9875                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9876                 if (target_locked) 
9877                         flag.perm_flags |= MSG_LOCKED;
9878         }
9879         flag.tmp_flags = MSG_DRAFT;
9880
9881         folder_item_scan(draft);
9882         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9883                 MsgInfo *tmpinfo = NULL;
9884                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9885                 if (compose->msgid) {
9886                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9887                 }
9888                 if (tmpinfo) {
9889                         msgnum = tmpinfo->msgnum;
9890                         procmsg_msginfo_free(tmpinfo);
9891                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9892                 } else {
9893                         debug_print("didn't get draft msgnum after scanning\n");
9894                 }
9895         } else {
9896                 debug_print("got draft msgnum %d from adding\n", msgnum);
9897         }
9898         if (msgnum < 0) {
9899 warn_err:
9900                 claws_unlink(tmp);
9901                 g_free(tmp);
9902                 if (action != COMPOSE_AUTO_SAVE) {
9903                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9904                                 alertpanel_error(_("Could not save draft."));
9905                         else {
9906                                 AlertValue val;
9907                                 gtkut_window_popup(compose->window);
9908                                 val = alertpanel_full(_("Could not save draft"),
9909                                         _("Could not save draft.\n"
9910                                         "Do you want to cancel exit or discard this email?"),
9911                                           _("_Cancel exit"), _("_Discard email"), NULL,
9912                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9913                                 if (val == G_ALERTALTERNATE) {
9914                                         lock = FALSE;
9915                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9916                                         compose_close(compose);
9917                                         return TRUE;
9918                                 } else {
9919                                         lock = FALSE;
9920                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9921                                         return FALSE;
9922                                 }
9923                         }
9924                 }
9925                 goto unlock;
9926         }
9927         g_free(tmp);
9928
9929         if (compose->mode == COMPOSE_REEDIT) {
9930                 compose_remove_reedit_target(compose, TRUE);
9931         }
9932
9933         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9934
9935         if (newmsginfo) {
9936                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9937                 if (target_locked)
9938                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
9939                 else
9940                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
9941                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9942                         procmsg_msginfo_set_flags(newmsginfo, 0,
9943                                                   MSG_HAS_ATTACHMENT);
9944
9945                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9946                         compose_register_draft(newmsginfo);
9947                 }
9948                 procmsg_msginfo_free(newmsginfo);
9949         }
9950         
9951         folder_item_scan(draft);
9952         
9953         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9954                 lock = FALSE;
9955                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9956                 compose_close(compose);
9957                 return TRUE;
9958         } else {
9959                 struct stat s;
9960                 gchar *path;
9961
9962                 path = folder_item_fetch_msg(draft, msgnum);
9963                 if (path == NULL) {
9964                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9965                         goto unlock;
9966                 }
9967                 if (g_stat(path, &s) < 0) {
9968                         FILE_OP_ERROR(path, "stat");
9969                         g_free(path);
9970                         goto unlock;
9971                 }
9972                 g_free(path);
9973
9974                 procmsg_msginfo_free(compose->targetinfo);
9975                 compose->targetinfo = procmsg_msginfo_new();
9976                 compose->targetinfo->msgnum = msgnum;
9977                 compose->targetinfo->size = (goffset)s.st_size;
9978                 compose->targetinfo->mtime = s.st_mtime;
9979                 compose->targetinfo->folder = draft;
9980                 if (target_locked)
9981                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9982                 compose->mode = COMPOSE_REEDIT;
9983                 
9984                 if (action == COMPOSE_AUTO_SAVE) {
9985                         compose->autosaved_draft = compose->targetinfo;
9986                 }
9987                 compose->modified = FALSE;
9988                 compose_set_title(compose);
9989         }
9990 unlock:
9991         lock = FALSE;
9992         g_mutex_unlock(compose->mutex);
9993         return TRUE;
9994 }
9995
9996 void compose_clear_exit_drafts(void)
9997 {
9998         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9999                                       DRAFTED_AT_EXIT, NULL);
10000         if (is_file_exist(filepath))
10001                 claws_unlink(filepath);
10002         
10003         g_free(filepath);
10004 }
10005
10006 void compose_reopen_exit_drafts(void)
10007 {
10008         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10009                                       DRAFTED_AT_EXIT, NULL);
10010         FILE *fp = g_fopen(filepath, "rb");
10011         gchar buf[1024];
10012         
10013         if (fp) {
10014                 while (fgets(buf, sizeof(buf), fp)) {
10015                         gchar **parts = g_strsplit(buf, "\t", 2);
10016                         const gchar *folder = parts[0];
10017                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10018                         
10019                         if (folder && *folder && msgnum > -1) {
10020                                 FolderItem *item = folder_find_item_from_identifier(folder);
10021                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10022                                 if (info)
10023                                         compose_reedit(info, FALSE);
10024                         }
10025                         g_strfreev(parts);
10026                 }       
10027                 fclose(fp);
10028         }       
10029         g_free(filepath);
10030         compose_clear_exit_drafts();
10031 }
10032
10033 static void compose_save_cb(GtkAction *action, gpointer data)
10034 {
10035         Compose *compose = (Compose *)data;
10036         compose_draft(compose, COMPOSE_KEEP_EDITING);
10037         compose->rmode = COMPOSE_REEDIT;
10038 }
10039
10040 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10041 {
10042         if (compose && file_list) {
10043                 GList *tmp;
10044
10045                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10046                         gchar *file = (gchar *) tmp->data;
10047                         gchar *utf8_filename = conv_filename_to_utf8(file);
10048                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10049                         compose_changed_cb(NULL, compose);
10050                         if (free_data) {
10051                         g_free(file);
10052                                 tmp->data = NULL;
10053                         }
10054                         g_free(utf8_filename);
10055                 }
10056         }
10057 }
10058
10059 static void compose_attach_cb(GtkAction *action, gpointer data)
10060 {
10061         Compose *compose = (Compose *)data;
10062         GList *file_list;
10063
10064         if (compose->redirect_filename != NULL)
10065                 return;
10066
10067         /* Set focus_window properly, in case we were called via popup menu,
10068          * which unsets it (via focus_out_event callback on compose window). */
10069         manage_window_focus_in(compose->window, NULL, NULL);
10070
10071         file_list = filesel_select_multiple_files_open(_("Select file"));
10072
10073         if (file_list) {
10074                 compose_attach_from_list(compose, file_list, TRUE);
10075                 g_list_free(file_list);
10076         }
10077 }
10078
10079 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10080 {
10081         Compose *compose = (Compose *)data;
10082         GList *file_list;
10083         gint files_inserted = 0;
10084
10085         file_list = filesel_select_multiple_files_open(_("Select file"));
10086
10087         if (file_list) {
10088                 GList *tmp;
10089
10090                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10091                         gchar *file = (gchar *) tmp->data;
10092                         gchar *filedup = g_strdup(file);
10093                         gchar *shortfile = g_path_get_basename(filedup);
10094                         ComposeInsertResult res;
10095                         /* insert the file if the file is short or if the user confirmed that
10096                            he/she wants to insert the large file */
10097                         res = compose_insert_file(compose, file);
10098                         if (res == COMPOSE_INSERT_READ_ERROR) {
10099                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10100                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10101                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10102                                                         "for the current encoding, insertion may be incorrect."),
10103                                                         shortfile);
10104                         } else if (res == COMPOSE_INSERT_SUCCESS)
10105                                 files_inserted++;
10106
10107                         g_free(shortfile);
10108                         g_free(filedup);
10109                         g_free(file);
10110                 }
10111                 g_list_free(file_list);
10112         }
10113
10114 #ifdef USE_ENCHANT      
10115         if (files_inserted > 0 && compose->gtkaspell && 
10116             compose->gtkaspell->check_while_typing)
10117                 gtkaspell_highlight_all(compose->gtkaspell);
10118 #endif
10119 }
10120
10121 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10122 {
10123         Compose *compose = (Compose *)data;
10124
10125         compose_insert_sig(compose, FALSE);
10126 }
10127
10128 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10129 {
10130         Compose *compose = (Compose *)data;
10131
10132         compose_insert_sig(compose, TRUE);
10133 }
10134
10135 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10136                               gpointer data)
10137 {
10138         gint x, y;
10139         Compose *compose = (Compose *)data;
10140
10141         gtkut_widget_get_uposition(widget, &x, &y);
10142         if (!compose->batch) {
10143                 prefs_common.compose_x = x;
10144                 prefs_common.compose_y = y;
10145         }
10146         if (compose->sending || compose->updating)
10147                 return TRUE;
10148         compose_close_cb(NULL, compose);
10149         return TRUE;
10150 }
10151
10152 void compose_close_toolbar(Compose *compose)
10153 {
10154         compose_close_cb(NULL, compose);
10155 }
10156
10157 static gboolean compose_can_autosave(Compose *compose)
10158 {
10159         if (compose->privacy_system && compose->use_encryption)
10160                 return prefs_common.autosave && prefs_common.autosave_encrypted;
10161         else
10162                 return prefs_common.autosave;
10163 }
10164
10165 static void compose_close_cb(GtkAction *action, gpointer data)
10166 {
10167         Compose *compose = (Compose *)data;
10168         AlertValue val;
10169
10170 #ifdef G_OS_UNIX
10171         if (compose->exteditor_tag != -1) {
10172                 if (!compose_ext_editor_kill(compose))
10173                         return;
10174         }
10175 #endif
10176
10177         if (compose->modified) {
10178                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10179                 if (!g_mutex_trylock(compose->mutex)) {
10180                         /* we don't want to lock the mutex once it's available,
10181                          * because as the only other part of compose.c locking
10182                          * it is compose_close - which means once unlocked,
10183                          * the compose struct will be freed */
10184                         debug_print("couldn't lock mutex, probably sending\n");
10185                         return;
10186                 }
10187                 if (!reedit) {
10188                         val = alertpanel(_("Discard message"),
10189                                  _("This message has been modified. Discard it?"),
10190                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10191                 } else {
10192                         val = alertpanel(_("Save changes"),
10193                                  _("This message has been modified. Save the latest changes?"),
10194                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10195                 }
10196                 g_mutex_unlock(compose->mutex);
10197                 switch (val) {
10198                 case G_ALERTDEFAULT:
10199                         if (compose_can_autosave(compose) && !reedit)
10200                                 compose_remove_draft(compose);
10201                         break;
10202                 case G_ALERTALTERNATE:
10203                         compose_draft(data, COMPOSE_QUIT_EDITING);
10204                         return;
10205                 default:
10206                         return;
10207                 }
10208         }
10209
10210         compose_close(compose);
10211 }
10212
10213 static void compose_print_cb(GtkAction *action, gpointer data)
10214 {
10215         Compose *compose = (Compose *) data;
10216
10217         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10218         if (compose->targetinfo)
10219                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10220 }
10221
10222 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10223 {
10224         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10225         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10226         Compose *compose = (Compose *) data;
10227
10228         if (active)
10229                 compose->out_encoding = (CharSet)value;
10230 }
10231
10232 static void compose_address_cb(GtkAction *action, gpointer data)
10233 {
10234         Compose *compose = (Compose *)data;
10235
10236 #ifndef USE_NEW_ADDRBOOK
10237         addressbook_open(compose);
10238 #else
10239         GError* error = NULL;
10240         addressbook_connect_signals(compose);
10241         addressbook_dbus_open(TRUE, &error);
10242         if (error) {
10243                 g_warning("%s", error->message);
10244                 g_error_free(error);
10245         }
10246 #endif
10247 }
10248
10249 static void about_show_cb(GtkAction *action, gpointer data)
10250 {
10251         about_show();
10252 }
10253
10254 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10255 {
10256         Compose *compose = (Compose *)data;
10257         Template *tmpl;
10258         gchar *msg;
10259         AlertValue val;
10260
10261         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10262         cm_return_if_fail(tmpl != NULL);
10263
10264         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10265                               tmpl->name);
10266         val = alertpanel(_("Apply template"), msg,
10267                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10268         g_free(msg);
10269
10270         if (val == G_ALERTDEFAULT)
10271                 compose_template_apply(compose, tmpl, TRUE);
10272         else if (val == G_ALERTALTERNATE)
10273                 compose_template_apply(compose, tmpl, FALSE);
10274 }
10275
10276 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10277 {
10278         Compose *compose = (Compose *)data;
10279
10280         compose_exec_ext_editor(compose);
10281 }
10282
10283 static void compose_undo_cb(GtkAction *action, gpointer data)
10284 {
10285         Compose *compose = (Compose *)data;
10286         gboolean prev_autowrap = compose->autowrap;
10287
10288         compose->autowrap = FALSE;
10289         undo_undo(compose->undostruct);
10290         compose->autowrap = prev_autowrap;
10291 }
10292
10293 static void compose_redo_cb(GtkAction *action, gpointer data)
10294 {
10295         Compose *compose = (Compose *)data;
10296         gboolean prev_autowrap = compose->autowrap;
10297         
10298         compose->autowrap = FALSE;
10299         undo_redo(compose->undostruct);
10300         compose->autowrap = prev_autowrap;
10301 }
10302
10303 static void entry_cut_clipboard(GtkWidget *entry)
10304 {
10305         if (GTK_IS_EDITABLE(entry))
10306                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10307         else if (GTK_IS_TEXT_VIEW(entry))
10308                 gtk_text_buffer_cut_clipboard(
10309                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10310                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10311                         TRUE);
10312 }
10313
10314 static void entry_copy_clipboard(GtkWidget *entry)
10315 {
10316         if (GTK_IS_EDITABLE(entry))
10317                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10318         else if (GTK_IS_TEXT_VIEW(entry))
10319                 gtk_text_buffer_copy_clipboard(
10320                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10321                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10322 }
10323
10324 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10325                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10326 {
10327         if (GTK_IS_TEXT_VIEW(entry)) {
10328                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10329                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10330                 GtkTextIter start_iter, end_iter;
10331                 gint start, end;
10332                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10333
10334                 if (contents == NULL)
10335                         return;
10336         
10337                 /* we shouldn't delete the selection when middle-click-pasting, or we
10338                  * can't mid-click-paste our own selection */
10339                 if (clip != GDK_SELECTION_PRIMARY) {
10340                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10341                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10342                 }
10343                 
10344                 if (insert_place == NULL) {
10345                         /* if insert_place isn't specified, insert at the cursor.
10346                          * used for Ctrl-V pasting */
10347                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10348                         start = gtk_text_iter_get_offset(&start_iter);
10349                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10350                 } else {
10351                         /* if insert_place is specified, paste here.
10352                          * used for mid-click-pasting */
10353                         start = gtk_text_iter_get_offset(insert_place);
10354                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10355                         if (prefs_common.primary_paste_unselects)
10356                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10357                 }
10358                 
10359                 if (!wrap) {
10360                         /* paste unwrapped: mark the paste so it's not wrapped later */
10361                         end = start + strlen(contents);
10362                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10363                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10364                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10365                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10366                         /* rewrap paragraph now (after a mid-click-paste) */
10367                         mark_start = gtk_text_buffer_get_insert(buffer);
10368                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10369                         gtk_text_iter_backward_char(&start_iter);
10370                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10371                 }
10372         } else if (GTK_IS_EDITABLE(entry))
10373                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10374
10375         compose->modified = TRUE;
10376 }
10377
10378 static void entry_allsel(GtkWidget *entry)
10379 {
10380         if (GTK_IS_EDITABLE(entry))
10381                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10382         else if (GTK_IS_TEXT_VIEW(entry)) {
10383                 GtkTextIter startiter, enditer;
10384                 GtkTextBuffer *textbuf;
10385
10386                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10387                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10388                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10389
10390                 gtk_text_buffer_move_mark_by_name(textbuf, 
10391                         "selection_bound", &startiter);
10392                 gtk_text_buffer_move_mark_by_name(textbuf, 
10393                         "insert", &enditer);
10394         }
10395 }
10396
10397 static void compose_cut_cb(GtkAction *action, gpointer data)
10398 {
10399         Compose *compose = (Compose *)data;
10400         if (compose->focused_editable 
10401 #ifndef GENERIC_UMPC
10402             && gtk_widget_has_focus(compose->focused_editable)
10403 #endif
10404             )
10405                 entry_cut_clipboard(compose->focused_editable);
10406 }
10407
10408 static void compose_copy_cb(GtkAction *action, gpointer data)
10409 {
10410         Compose *compose = (Compose *)data;
10411         if (compose->focused_editable 
10412 #ifndef GENERIC_UMPC
10413             && gtk_widget_has_focus(compose->focused_editable)
10414 #endif
10415             )
10416                 entry_copy_clipboard(compose->focused_editable);
10417 }
10418
10419 static void compose_paste_cb(GtkAction *action, gpointer data)
10420 {
10421         Compose *compose = (Compose *)data;
10422         gint prev_autowrap;
10423         GtkTextBuffer *buffer;
10424         BLOCK_WRAP();
10425         if (compose->focused_editable &&
10426 #ifndef GENERIC_UMPC
10427             gtk_widget_has_focus(compose->focused_editable)
10428 #endif
10429                 )
10430                 entry_paste_clipboard(compose, compose->focused_editable, 
10431                                 prefs_common.linewrap_pastes,
10432                                 GDK_SELECTION_CLIPBOARD, NULL);
10433         UNBLOCK_WRAP();
10434
10435 #ifdef USE_ENCHANT
10436         if (
10437 #ifndef GENERIC_UMPC
10438                 gtk_widget_has_focus(compose->text) &&
10439 #endif
10440             compose->gtkaspell && 
10441             compose->gtkaspell->check_while_typing)
10442                 gtkaspell_highlight_all(compose->gtkaspell);
10443 #endif
10444 }
10445
10446 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10447 {
10448         Compose *compose = (Compose *)data;
10449         gint wrap_quote = prefs_common.linewrap_quote;
10450         if (compose->focused_editable 
10451 #ifndef GENERIC_UMPC
10452             && gtk_widget_has_focus(compose->focused_editable)
10453 #endif
10454             ) {
10455                 /* let text_insert() (called directly or at a later time
10456                  * after the gtk_editable_paste_clipboard) know that 
10457                  * text is to be inserted as a quotation. implemented
10458                  * by using a simple refcount... */
10459                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10460                                                 G_OBJECT(compose->focused_editable),
10461                                                 "paste_as_quotation"));
10462                 g_object_set_data(G_OBJECT(compose->focused_editable),
10463                                     "paste_as_quotation",
10464                                     GINT_TO_POINTER(paste_as_quotation + 1));
10465                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10466                 entry_paste_clipboard(compose, compose->focused_editable, 
10467                                 prefs_common.linewrap_pastes,
10468                                 GDK_SELECTION_CLIPBOARD, NULL);
10469                 prefs_common.linewrap_quote = wrap_quote;
10470         }
10471 }
10472
10473 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10474 {
10475         Compose *compose = (Compose *)data;
10476         gint prev_autowrap;
10477         GtkTextBuffer *buffer;
10478         BLOCK_WRAP();
10479         if (compose->focused_editable 
10480 #ifndef GENERIC_UMPC
10481             && gtk_widget_has_focus(compose->focused_editable)
10482 #endif
10483             )
10484                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10485                         GDK_SELECTION_CLIPBOARD, NULL);
10486         UNBLOCK_WRAP();
10487
10488 #ifdef USE_ENCHANT
10489         if (
10490 #ifndef GENERIC_UMPC
10491                 gtk_widget_has_focus(compose->text) &&
10492 #endif
10493             compose->gtkaspell && 
10494             compose->gtkaspell->check_while_typing)
10495                 gtkaspell_highlight_all(compose->gtkaspell);
10496 #endif
10497 }
10498
10499 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10500 {
10501         Compose *compose = (Compose *)data;
10502         gint prev_autowrap;
10503         GtkTextBuffer *buffer;
10504         BLOCK_WRAP();
10505         if (compose->focused_editable 
10506 #ifndef GENERIC_UMPC
10507             && gtk_widget_has_focus(compose->focused_editable)
10508 #endif
10509             )
10510                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10511                         GDK_SELECTION_CLIPBOARD, NULL);
10512         UNBLOCK_WRAP();
10513
10514 #ifdef USE_ENCHANT
10515         if (
10516 #ifndef GENERIC_UMPC
10517                 gtk_widget_has_focus(compose->text) &&
10518 #endif
10519             compose->gtkaspell &&
10520             compose->gtkaspell->check_while_typing)
10521                 gtkaspell_highlight_all(compose->gtkaspell);
10522 #endif
10523 }
10524
10525 static void compose_allsel_cb(GtkAction *action, gpointer data)
10526 {
10527         Compose *compose = (Compose *)data;
10528         if (compose->focused_editable 
10529 #ifndef GENERIC_UMPC
10530             && gtk_widget_has_focus(compose->focused_editable)
10531 #endif
10532             )
10533                 entry_allsel(compose->focused_editable);
10534 }
10535
10536 static void textview_move_beginning_of_line (GtkTextView *text)
10537 {
10538         GtkTextBuffer *buffer;
10539         GtkTextMark *mark;
10540         GtkTextIter ins;
10541
10542         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10543
10544         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10545         mark = gtk_text_buffer_get_insert(buffer);
10546         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10547         gtk_text_iter_set_line_offset(&ins, 0);
10548         gtk_text_buffer_place_cursor(buffer, &ins);
10549 }
10550
10551 static void textview_move_forward_character (GtkTextView *text)
10552 {
10553         GtkTextBuffer *buffer;
10554         GtkTextMark *mark;
10555         GtkTextIter ins;
10556
10557         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10558
10559         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10560         mark = gtk_text_buffer_get_insert(buffer);
10561         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10562         if (gtk_text_iter_forward_cursor_position(&ins))
10563                 gtk_text_buffer_place_cursor(buffer, &ins);
10564 }
10565
10566 static void textview_move_backward_character (GtkTextView *text)
10567 {
10568         GtkTextBuffer *buffer;
10569         GtkTextMark *mark;
10570         GtkTextIter ins;
10571
10572         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10573
10574         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10575         mark = gtk_text_buffer_get_insert(buffer);
10576         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10577         if (gtk_text_iter_backward_cursor_position(&ins))
10578                 gtk_text_buffer_place_cursor(buffer, &ins);
10579 }
10580
10581 static void textview_move_forward_word (GtkTextView *text)
10582 {
10583         GtkTextBuffer *buffer;
10584         GtkTextMark *mark;
10585         GtkTextIter ins;
10586         gint count;
10587
10588         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10589
10590         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10591         mark = gtk_text_buffer_get_insert(buffer);
10592         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10593         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10594         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10595                 gtk_text_iter_backward_word_start(&ins);
10596                 gtk_text_buffer_place_cursor(buffer, &ins);
10597         }
10598 }
10599
10600 static void textview_move_backward_word (GtkTextView *text)
10601 {
10602         GtkTextBuffer *buffer;
10603         GtkTextMark *mark;
10604         GtkTextIter ins;
10605
10606         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10607
10608         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10609         mark = gtk_text_buffer_get_insert(buffer);
10610         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10611         if (gtk_text_iter_backward_word_starts(&ins, 1))
10612                 gtk_text_buffer_place_cursor(buffer, &ins);
10613 }
10614
10615 static void textview_move_end_of_line (GtkTextView *text)
10616 {
10617         GtkTextBuffer *buffer;
10618         GtkTextMark *mark;
10619         GtkTextIter ins;
10620
10621         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10622
10623         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10624         mark = gtk_text_buffer_get_insert(buffer);
10625         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10626         if (gtk_text_iter_forward_to_line_end(&ins))
10627                 gtk_text_buffer_place_cursor(buffer, &ins);
10628 }
10629
10630 static void textview_move_next_line (GtkTextView *text)
10631 {
10632         GtkTextBuffer *buffer;
10633         GtkTextMark *mark;
10634         GtkTextIter ins;
10635         gint offset;
10636
10637         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10638
10639         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10640         mark = gtk_text_buffer_get_insert(buffer);
10641         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10642         offset = gtk_text_iter_get_line_offset(&ins);
10643         if (gtk_text_iter_forward_line(&ins)) {
10644                 gtk_text_iter_set_line_offset(&ins, offset);
10645                 gtk_text_buffer_place_cursor(buffer, &ins);
10646         }
10647 }
10648
10649 static void textview_move_previous_line (GtkTextView *text)
10650 {
10651         GtkTextBuffer *buffer;
10652         GtkTextMark *mark;
10653         GtkTextIter ins;
10654         gint offset;
10655
10656         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10657
10658         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10659         mark = gtk_text_buffer_get_insert(buffer);
10660         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10661         offset = gtk_text_iter_get_line_offset(&ins);
10662         if (gtk_text_iter_backward_line(&ins)) {
10663                 gtk_text_iter_set_line_offset(&ins, offset);
10664                 gtk_text_buffer_place_cursor(buffer, &ins);
10665         }
10666 }
10667
10668 static void textview_delete_forward_character (GtkTextView *text)
10669 {
10670         GtkTextBuffer *buffer;
10671         GtkTextMark *mark;
10672         GtkTextIter ins, end_iter;
10673
10674         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10675
10676         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10677         mark = gtk_text_buffer_get_insert(buffer);
10678         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10679         end_iter = ins;
10680         if (gtk_text_iter_forward_char(&end_iter)) {
10681                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10682         }
10683 }
10684
10685 static void textview_delete_backward_character (GtkTextView *text)
10686 {
10687         GtkTextBuffer *buffer;
10688         GtkTextMark *mark;
10689         GtkTextIter ins, end_iter;
10690
10691         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10692
10693         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10694         mark = gtk_text_buffer_get_insert(buffer);
10695         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10696         end_iter = ins;
10697         if (gtk_text_iter_backward_char(&end_iter)) {
10698                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10699         }
10700 }
10701
10702 static void textview_delete_forward_word (GtkTextView *text)
10703 {
10704         GtkTextBuffer *buffer;
10705         GtkTextMark *mark;
10706         GtkTextIter ins, end_iter;
10707
10708         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10709
10710         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10711         mark = gtk_text_buffer_get_insert(buffer);
10712         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10713         end_iter = ins;
10714         if (gtk_text_iter_forward_word_end(&end_iter)) {
10715                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10716         }
10717 }
10718
10719 static void textview_delete_backward_word (GtkTextView *text)
10720 {
10721         GtkTextBuffer *buffer;
10722         GtkTextMark *mark;
10723         GtkTextIter ins, end_iter;
10724
10725         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10726
10727         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10728         mark = gtk_text_buffer_get_insert(buffer);
10729         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10730         end_iter = ins;
10731         if (gtk_text_iter_backward_word_start(&end_iter)) {
10732                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10733         }
10734 }
10735
10736 static void textview_delete_line (GtkTextView *text)
10737 {
10738         GtkTextBuffer *buffer;
10739         GtkTextMark *mark;
10740         GtkTextIter ins, start_iter, end_iter;
10741
10742         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10743
10744         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10745         mark = gtk_text_buffer_get_insert(buffer);
10746         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10747
10748         start_iter = ins;
10749         gtk_text_iter_set_line_offset(&start_iter, 0);
10750
10751         end_iter = ins;
10752         if (gtk_text_iter_ends_line(&end_iter)){
10753                 if (!gtk_text_iter_forward_char(&end_iter))
10754                         gtk_text_iter_backward_char(&start_iter);
10755         }
10756         else 
10757                 gtk_text_iter_forward_to_line_end(&end_iter);
10758         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10759 }
10760
10761 static void textview_delete_to_line_end (GtkTextView *text)
10762 {
10763         GtkTextBuffer *buffer;
10764         GtkTextMark *mark;
10765         GtkTextIter ins, end_iter;
10766
10767         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10768
10769         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10770         mark = gtk_text_buffer_get_insert(buffer);
10771         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10772         end_iter = ins;
10773         if (gtk_text_iter_ends_line(&end_iter))
10774                 gtk_text_iter_forward_char(&end_iter);
10775         else
10776                 gtk_text_iter_forward_to_line_end(&end_iter);
10777         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10778 }
10779
10780 #define DO_ACTION(name, act) {                                          \
10781         if(!strcmp(name, a_name)) {                                     \
10782                 return act;                                             \
10783         }                                                               \
10784 }
10785 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10786 {
10787         const gchar *a_name = gtk_action_get_name(action);
10788         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10789         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10790         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10791         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10792         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10793         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10794         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10795         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10796         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10797         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10798         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10799         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10800         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10801         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10802         return -1;
10803 }
10804
10805 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10806 {
10807         Compose *compose = (Compose *)data;
10808         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10809         ComposeCallAdvancedAction action = -1;
10810         
10811         action = compose_call_advanced_action_from_path(gaction);
10812
10813         static struct {
10814                 void (*do_action) (GtkTextView *text);
10815         } action_table[] = {
10816                 {textview_move_beginning_of_line},
10817                 {textview_move_forward_character},
10818                 {textview_move_backward_character},
10819                 {textview_move_forward_word},
10820                 {textview_move_backward_word},
10821                 {textview_move_end_of_line},
10822                 {textview_move_next_line},
10823                 {textview_move_previous_line},
10824                 {textview_delete_forward_character},
10825                 {textview_delete_backward_character},
10826                 {textview_delete_forward_word},
10827                 {textview_delete_backward_word},
10828                 {textview_delete_line},
10829                 {textview_delete_to_line_end}
10830         };
10831
10832         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10833
10834         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10835             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10836                 if (action_table[action].do_action)
10837                         action_table[action].do_action(text);
10838                 else
10839                         g_warning("Not implemented yet.");
10840         }
10841 }
10842
10843 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10844 {
10845         GtkAllocation allocation;
10846         GtkWidget *parent;
10847         gchar *str = NULL;
10848         
10849         if (GTK_IS_EDITABLE(widget)) {
10850                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10851                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10852                         strlen(str));
10853                 g_free(str);
10854                 if ((parent = gtk_widget_get_parent(widget))
10855                  && (parent = gtk_widget_get_parent(parent))
10856                  && (parent = gtk_widget_get_parent(parent))) {
10857                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
10858                                 gtk_widget_get_allocation(widget, &allocation);
10859                                 gint y = allocation.y;
10860                                 gint height = allocation.height;
10861                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10862                                         (GTK_SCROLLED_WINDOW(parent));
10863
10864                                 gfloat value = gtk_adjustment_get_value(shown);
10865                                 gfloat upper = gtk_adjustment_get_upper(shown);
10866                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
10867                                 if (y < (int)value) {
10868                                         gtk_adjustment_set_value(shown, y - 1);
10869                                 }
10870                                 if ((y + height) > ((int)value + (int)page_size)) {
10871                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
10872                                                 gtk_adjustment_set_value(shown, 
10873                                                         y + height - (int)page_size - 1);
10874                                         } else {
10875                                                 gtk_adjustment_set_value(shown, 
10876                                                         (int)upper - (int)page_size - 1);
10877                                         }
10878                                 }
10879                         }
10880                 }
10881         }
10882
10883         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10884                 compose->focused_editable = widget;
10885         
10886 #ifdef GENERIC_UMPC
10887         if (GTK_IS_TEXT_VIEW(widget) 
10888             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10889                 g_object_ref(compose->notebook);
10890                 g_object_ref(compose->edit_vbox);
10891                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10892                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10893                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10894                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10895                 g_object_unref(compose->notebook);
10896                 g_object_unref(compose->edit_vbox);
10897                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10898                                         G_CALLBACK(compose_grab_focus_cb),
10899                                         compose);
10900                 gtk_widget_grab_focus(widget);
10901                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10902                                         G_CALLBACK(compose_grab_focus_cb),
10903                                         compose);
10904         } else if (!GTK_IS_TEXT_VIEW(widget) 
10905                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10906                 g_object_ref(compose->notebook);
10907                 g_object_ref(compose->edit_vbox);
10908                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10909                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10910                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10911                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10912                 g_object_unref(compose->notebook);
10913                 g_object_unref(compose->edit_vbox);
10914                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10915                                         G_CALLBACK(compose_grab_focus_cb),
10916                                         compose);
10917                 gtk_widget_grab_focus(widget);
10918                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10919                                         G_CALLBACK(compose_grab_focus_cb),
10920                                         compose);
10921         }
10922 #endif
10923 }
10924
10925 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10926 {
10927         compose->modified = TRUE;
10928 //      compose_beautify_paragraph(compose, NULL, TRUE);
10929 #ifndef GENERIC_UMPC
10930         compose_set_title(compose);
10931 #endif
10932 }
10933
10934 static void compose_wrap_cb(GtkAction *action, gpointer data)
10935 {
10936         Compose *compose = (Compose *)data;
10937         compose_beautify_paragraph(compose, NULL, TRUE);
10938 }
10939
10940 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10941 {
10942         Compose *compose = (Compose *)data;
10943         compose_wrap_all_full(compose, TRUE);
10944 }
10945
10946 static void compose_find_cb(GtkAction *action, gpointer data)
10947 {
10948         Compose *compose = (Compose *)data;
10949
10950         message_search_compose(compose);
10951 }
10952
10953 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10954                                          gpointer        data)
10955 {
10956         Compose *compose = (Compose *)data;
10957         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10958         if (compose->autowrap)
10959                 compose_wrap_all_full(compose, TRUE);
10960         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10961 }
10962
10963 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10964                                          gpointer        data)
10965 {
10966         Compose *compose = (Compose *)data;
10967         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10968 }
10969
10970 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10971 {
10972         Compose *compose = (Compose *)data;
10973
10974         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10975 }
10976
10977 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10978 {
10979         Compose *compose = (Compose *)data;
10980
10981         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10982 }
10983
10984 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10985 {
10986         g_free(compose->privacy_system);
10987
10988         compose->privacy_system = g_strdup(account->default_privacy_system);
10989         compose_update_privacy_system_menu_item(compose, warn);
10990 }
10991
10992 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10993 {
10994         Compose *compose = (Compose *)data;
10995
10996         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10997                 gtk_widget_show(compose->ruler_hbox);
10998                 prefs_common.show_ruler = TRUE;
10999         } else {
11000                 gtk_widget_hide(compose->ruler_hbox);
11001                 gtk_widget_queue_resize(compose->edit_vbox);
11002                 prefs_common.show_ruler = FALSE;
11003         }
11004 }
11005
11006 static void compose_attach_drag_received_cb (GtkWidget          *widget,
11007                                              GdkDragContext     *context,
11008                                              gint                x,
11009                                              gint                y,
11010                                              GtkSelectionData   *data,
11011                                              guint               info,
11012                                              guint               time,
11013                                              gpointer            user_data)
11014 {
11015         Compose *compose = (Compose *)user_data;
11016         GList *list, *tmp;
11017         GdkAtom type;
11018
11019         type = gtk_selection_data_get_data_type(data);
11020         if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11021 #ifdef G_OS_WIN32
11022          || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
11023 #endif
11024            ) && gtk_drag_get_source_widget(context) != 
11025                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11026                 list = uri_list_extract_filenames(
11027                         (const gchar *)gtk_selection_data_get_data(data));
11028                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11029                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11030                         compose_attach_append
11031                                 (compose, (const gchar *)tmp->data,
11032                                  utf8_filename, NULL, NULL);
11033                         g_free(utf8_filename);
11034                 }
11035                 if (list) compose_changed_cb(NULL, compose);
11036                 list_free_strings(list);
11037                 g_list_free(list);
11038         } else if (gtk_drag_get_source_widget(context) 
11039                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11040                 /* comes from our summaryview */
11041                 SummaryView * summaryview = NULL;
11042                 GSList * list = NULL, *cur = NULL;
11043                 
11044                 if (mainwindow_get_mainwindow())
11045                         summaryview = mainwindow_get_mainwindow()->summaryview;
11046                 
11047                 if (summaryview)
11048                         list = summary_get_selected_msg_list(summaryview);
11049                 
11050                 for (cur = list; cur; cur = cur->next) {
11051                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11052                         gchar *file = NULL;
11053                         if (msginfo)
11054                                 file = procmsg_get_message_file_full(msginfo, 
11055                                         TRUE, TRUE);
11056                         if (file) {
11057                                 compose_attach_append(compose, (const gchar *)file, 
11058                                         (const gchar *)file, "message/rfc822", NULL);
11059                                 g_free(file);
11060                         }
11061                 }
11062                 g_slist_free(list);
11063         }
11064 }
11065
11066 static gboolean compose_drag_drop(GtkWidget *widget,
11067                                   GdkDragContext *drag_context,
11068                                   gint x, gint y,
11069                                   guint time, gpointer user_data)
11070 {
11071         /* not handling this signal makes compose_insert_drag_received_cb
11072          * called twice */
11073         return TRUE;                                     
11074 }
11075
11076 static gboolean completion_set_focus_to_subject
11077                                         (GtkWidget    *widget,
11078                                          GdkEventKey  *event,
11079                                          Compose      *compose)
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         return FALSE;
11089 }
11090
11091 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11092                                              GdkDragContext     *drag_context,
11093                                              gint                x,
11094                                              gint                y,
11095                                              GtkSelectionData   *data,
11096                                              guint               info,
11097                                              guint               time,
11098                                              gpointer            user_data)
11099 {
11100         Compose *compose = (Compose *)user_data;
11101         GList *list, *tmp;
11102         GdkAtom type;
11103
11104         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11105          * does not work */
11106         type = gtk_selection_data_get_data_type(data);
11107 #ifndef G_OS_WIN32
11108         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11109 #else
11110         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
11111 #endif
11112                 AlertValue val = G_ALERTDEFAULT;
11113                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11114
11115                 list = uri_list_extract_filenames(ddata);
11116                 if (list == NULL && strstr(ddata, "://")) {
11117                         /* Assume a list of no files, and data has ://, is a remote link */
11118                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11119                         gchar *tmpfile = get_tmp_file();
11120                         str_write_to_file(tmpdata, tmpfile);
11121                         g_free(tmpdata);  
11122                         compose_insert_file(compose, tmpfile);
11123                         claws_unlink(tmpfile);
11124                         g_free(tmpfile);
11125                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11126                         compose_beautify_paragraph(compose, NULL, TRUE);
11127                         return;
11128                 }
11129                 switch (prefs_common.compose_dnd_mode) {
11130                         case COMPOSE_DND_ASK:
11131                                 val = alertpanel_full(_("Insert or attach?"),
11132                                          _("Do you want to insert the contents of the file(s) "
11133                                            "into the message body, or attach it to the email?"),
11134                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
11135                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11136                                 break;
11137                         case COMPOSE_DND_INSERT:
11138                                 val = G_ALERTALTERNATE;
11139                                 break;
11140                         case COMPOSE_DND_ATTACH:
11141                                 val = G_ALERTOTHER;
11142                                 break;
11143                         default:
11144                                 /* unexpected case */
11145                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11146                 }
11147
11148                 if (val & G_ALERTDISABLE) {
11149                         val &= ~G_ALERTDISABLE;
11150                         /* remember what action to perform by default, only if we don't click Cancel */
11151                         if (val == G_ALERTALTERNATE)
11152                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11153                         else if (val == G_ALERTOTHER)
11154                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11155                 }
11156
11157                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11158                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11159                         list_free_strings(list);
11160                         g_list_free(list);
11161                         return;
11162                 } else if (val == G_ALERTOTHER) {
11163                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11164                         list_free_strings(list);
11165                         g_list_free(list);
11166                         return;
11167                 } 
11168
11169                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11170                         compose_insert_file(compose, (const gchar *)tmp->data);
11171                 }
11172                 list_free_strings(list);
11173                 g_list_free(list);
11174                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11175                 return;
11176         }
11177 }
11178
11179 static void compose_header_drag_received_cb (GtkWidget          *widget,
11180                                              GdkDragContext     *drag_context,
11181                                              gint                x,
11182                                              gint                y,
11183                                              GtkSelectionData   *data,
11184                                              guint               info,
11185                                              guint               time,
11186                                              gpointer            user_data)
11187 {
11188         GtkEditable *entry = (GtkEditable *)user_data;
11189         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11190
11191         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11192          * does not work */
11193
11194         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11195                 gchar *decoded=g_new(gchar, strlen(email));
11196                 int start = 0;
11197
11198                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11199                 gtk_editable_delete_text(entry, 0, -1);
11200                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11201                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11202                 g_free(decoded);
11203                 return;
11204         }
11205         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11206 }
11207
11208 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11209 {
11210         Compose *compose = (Compose *)data;
11211
11212         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11213                 compose->return_receipt = TRUE;
11214         else
11215                 compose->return_receipt = FALSE;
11216 }
11217
11218 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11219 {
11220         Compose *compose = (Compose *)data;
11221
11222         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11223                 compose->remove_references = TRUE;
11224         else
11225                 compose->remove_references = FALSE;
11226 }
11227
11228 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11229                                         ComposeHeaderEntry *headerentry)
11230 {
11231         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11232         return FALSE;
11233 }
11234
11235 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11236                                             GdkEventKey *event,
11237                                             ComposeHeaderEntry *headerentry)
11238 {
11239         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11240             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11241             !(event->state & GDK_MODIFIER_MASK) &&
11242             (event->keyval == GDK_KEY_BackSpace) &&
11243             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11244                 gtk_container_remove
11245                         (GTK_CONTAINER(headerentry->compose->header_table),
11246                          headerentry->combo);
11247                 gtk_container_remove
11248                         (GTK_CONTAINER(headerentry->compose->header_table),
11249                          headerentry->entry);
11250                 headerentry->compose->header_list =
11251                         g_slist_remove(headerentry->compose->header_list,
11252                                        headerentry);
11253                 g_free(headerentry);
11254         } else  if (event->keyval == GDK_KEY_Tab) {
11255                 if (headerentry->compose->header_last == headerentry) {
11256                         /* Override default next focus, and give it to subject_entry
11257                          * instead of notebook tabs
11258                          */
11259                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11260                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11261                         return TRUE;
11262                 }
11263         }
11264         return FALSE;
11265 }
11266
11267 static gboolean scroll_postpone(gpointer data)
11268 {
11269         Compose *compose = (Compose *)data;
11270
11271         if (compose->batch)
11272                 return FALSE;
11273
11274         GTK_EVENTS_FLUSH();
11275         compose_show_first_last_header(compose, FALSE);
11276         return FALSE;
11277 }
11278
11279 static void compose_headerentry_changed_cb(GtkWidget *entry,
11280                                     ComposeHeaderEntry *headerentry)
11281 {
11282         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11283                 compose_create_header_entry(headerentry->compose);
11284                 g_signal_handlers_disconnect_matched
11285                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11286                          0, 0, NULL, NULL, headerentry);
11287
11288                 if (!headerentry->compose->batch)
11289                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11290         }
11291 }
11292
11293 static gboolean compose_defer_auto_save_draft(Compose *compose)
11294 {
11295         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11296         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11297         return FALSE;
11298 }
11299
11300 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11301 {
11302         GtkAdjustment *vadj;
11303
11304         cm_return_if_fail(compose);
11305
11306         if(compose->batch)
11307                 return;
11308
11309         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11310         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11311         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11312                                 gtk_widget_get_parent(compose->header_table)));
11313         gtk_adjustment_set_value(vadj, (show_first ?
11314                                 gtk_adjustment_get_lower(vadj) :
11315                                 (gtk_adjustment_get_upper(vadj) -
11316                                 gtk_adjustment_get_page_size(vadj))));
11317         gtk_adjustment_changed(vadj);
11318 }
11319
11320 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11321                           const gchar *text, gint len, Compose *compose)
11322 {
11323         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11324                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11325         GtkTextMark *mark;
11326
11327         cm_return_if_fail(text != NULL);
11328
11329         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11330                                         G_CALLBACK(text_inserted),
11331                                         compose);
11332         if (paste_as_quotation) {
11333                 gchar *new_text;
11334                 const gchar *qmark;
11335                 guint pos = 0;
11336                 GtkTextIter start_iter;
11337
11338                 if (len < 0)
11339                         len = strlen(text);
11340
11341                 new_text = g_strndup(text, len);
11342
11343                 qmark = compose_quote_char_from_context(compose);
11344
11345                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11346                 gtk_text_buffer_place_cursor(buffer, iter);
11347
11348                 pos = gtk_text_iter_get_offset(iter);
11349
11350                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11351                                                   _("Quote format error at line %d."));
11352                 quote_fmt_reset_vartable();
11353                 g_free(new_text);
11354                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11355                                   GINT_TO_POINTER(paste_as_quotation - 1));
11356                                   
11357                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11358                 gtk_text_buffer_place_cursor(buffer, iter);
11359                 gtk_text_buffer_delete_mark(buffer, mark);
11360
11361                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11362                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11363                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11364                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11365                 gtk_text_buffer_delete_mark(buffer, mark);
11366         } else {
11367                 if (strcmp(text, "\n") || compose->automatic_break
11368                 || gtk_text_iter_starts_line(iter)) {
11369                         GtkTextIter before_ins;
11370                         gtk_text_buffer_insert(buffer, iter, text, len);
11371                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11372                                 before_ins = *iter; 
11373                                 gtk_text_iter_backward_chars(&before_ins, len);
11374                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11375                         }
11376                 } else {
11377                         /* check if the preceding is just whitespace or quote */
11378                         GtkTextIter start_line;
11379                         gchar *tmp = NULL, *quote = NULL;
11380                         gint quote_len = 0, is_normal = 0;
11381                         start_line = *iter;
11382                         gtk_text_iter_set_line_offset(&start_line, 0); 
11383                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11384                         g_strstrip(tmp);
11385
11386                         if (*tmp == '\0') {
11387                                 is_normal = 1;
11388                         } else {
11389                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11390                                 if (quote)
11391                                         is_normal = 1;
11392                                 g_free(quote);
11393                         }
11394                         g_free(tmp);
11395                         
11396                         if (is_normal) {
11397                                 gtk_text_buffer_insert(buffer, iter, text, len);
11398                         } else {
11399                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11400                                         iter, text, len, "no_join", NULL);
11401                         }
11402                 }
11403         }
11404         
11405         if (!paste_as_quotation) {
11406                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11407                 compose_beautify_paragraph(compose, iter, FALSE);
11408                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11409                 gtk_text_buffer_delete_mark(buffer, mark);
11410         }
11411
11412         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11413                                           G_CALLBACK(text_inserted),
11414                                           compose);
11415         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11416
11417         if (compose_can_autosave(compose) && 
11418             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11419             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11420                 compose->draft_timeout_tag = g_timeout_add
11421                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11422 }
11423
11424 #if USE_ENCHANT
11425 static void compose_check_all(GtkAction *action, gpointer data)
11426 {
11427         Compose *compose = (Compose *)data;
11428         if (!compose->gtkaspell)
11429                 return;
11430                 
11431         if (gtk_widget_has_focus(compose->subject_entry))
11432                 claws_spell_entry_check_all(
11433                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11434         else
11435                 gtkaspell_check_all(compose->gtkaspell);
11436 }
11437
11438 static void compose_highlight_all(GtkAction *action, gpointer data)
11439 {
11440         Compose *compose = (Compose *)data;
11441         if (compose->gtkaspell) {
11442                 claws_spell_entry_recheck_all(
11443                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11444                 gtkaspell_highlight_all(compose->gtkaspell);
11445         }
11446 }
11447
11448 static void compose_check_backwards(GtkAction *action, gpointer data)
11449 {
11450         Compose *compose = (Compose *)data;
11451         if (!compose->gtkaspell) {
11452                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11453                 return;
11454         }
11455
11456         if (gtk_widget_has_focus(compose->subject_entry))
11457                 claws_spell_entry_check_backwards(
11458                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11459         else
11460                 gtkaspell_check_backwards(compose->gtkaspell);
11461 }
11462
11463 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11464 {
11465         Compose *compose = (Compose *)data;
11466         if (!compose->gtkaspell) {
11467                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11468                 return;
11469         }
11470
11471         if (gtk_widget_has_focus(compose->subject_entry))
11472                 claws_spell_entry_check_forwards_go(
11473                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11474         else
11475                 gtkaspell_check_forwards_go(compose->gtkaspell);
11476 }
11477 #endif
11478
11479 /*!
11480  *\brief        Guess originating forward account from MsgInfo and several 
11481  *              "common preference" settings. Return NULL if no guess. 
11482  */
11483 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11484 {
11485         PrefsAccount *account = NULL;
11486         
11487         cm_return_val_if_fail(msginfo, NULL);
11488         cm_return_val_if_fail(msginfo->folder, NULL);
11489         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11490
11491         if (msginfo->folder->prefs->enable_default_account)
11492                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11493                 
11494         if (!account) 
11495                 account = msginfo->folder->folder->account;
11496                 
11497         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11498                 gchar *to;
11499                 Xstrdup_a(to, msginfo->to, return NULL);
11500                 extract_address(to);
11501                 account = account_find_from_address(to, FALSE);
11502         }
11503
11504         if (!account && prefs_common.forward_account_autosel) {
11505                 gchar cc[BUFFSIZE];
11506                 if (!procheader_get_header_from_msginfo
11507                         (msginfo, cc,sizeof cc , "Cc:")) { 
11508                         gchar *buf = cc + strlen("Cc:");
11509                         extract_address(buf);
11510                         account = account_find_from_address(buf, FALSE);
11511                 }
11512         }
11513         
11514         if (!account && prefs_common.forward_account_autosel) {
11515                 gchar deliveredto[BUFFSIZE];
11516                 if (!procheader_get_header_from_msginfo
11517                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11518                         gchar *buf = deliveredto + strlen("Delivered-To:");
11519                         extract_address(buf);
11520                         account = account_find_from_address(buf, FALSE);
11521                 }
11522         }
11523         
11524         return account;
11525 }
11526
11527 gboolean compose_close(Compose *compose)
11528 {
11529         gint x, y;
11530
11531         cm_return_val_if_fail(compose, FALSE);
11532
11533         if (!g_mutex_trylock(compose->mutex)) {
11534                 /* we have to wait for the (possibly deferred by auto-save)
11535                  * drafting to be done, before destroying the compose under
11536                  * it. */
11537                 debug_print("waiting for drafting to finish...\n");
11538                 compose_allow_user_actions(compose, FALSE);
11539                 if (compose->close_timeout_tag == 0) {
11540                         compose->close_timeout_tag = 
11541                                 g_timeout_add (500, (GSourceFunc) compose_close,
11542                                 compose);
11543                 }
11544                 return TRUE;
11545         }
11546
11547         if (compose->draft_timeout_tag >= 0) {
11548                 g_source_remove(compose->draft_timeout_tag);
11549                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11550         }
11551
11552         gtkut_widget_get_uposition(compose->window, &x, &y);
11553         if (!compose->batch) {
11554                 prefs_common.compose_x = x;
11555                 prefs_common.compose_y = y;
11556         }
11557         g_mutex_unlock(compose->mutex);
11558         compose_destroy(compose);
11559         return FALSE;
11560 }
11561
11562 /**
11563  * Add entry field for each address in list.
11564  * \param compose     E-Mail composition object.
11565  * \param listAddress List of (formatted) E-Mail addresses.
11566  */
11567 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11568         GList *node;
11569         gchar *addr;
11570         node = listAddress;
11571         while( node ) {
11572                 addr = ( gchar * ) node->data;
11573                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11574                 node = g_list_next( node );
11575         }
11576 }
11577
11578 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11579                                     guint action, gboolean opening_multiple)
11580 {
11581         gchar *body = NULL;
11582         GSList *new_msglist = NULL;
11583         MsgInfo *tmp_msginfo = NULL;
11584         gboolean originally_enc = FALSE;
11585         gboolean originally_sig = FALSE;
11586         Compose *compose = NULL;
11587         gchar *s_system = NULL;
11588
11589         cm_return_if_fail(msgview != NULL);
11590
11591         cm_return_if_fail(msginfo_list != NULL);
11592
11593         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11594                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11595                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11596
11597                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11598                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11599                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11600                                                 orig_msginfo, mimeinfo);
11601                         if (tmp_msginfo != NULL) {
11602                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11603
11604                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11605                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11606                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11607
11608                                 tmp_msginfo->folder = orig_msginfo->folder;
11609                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11610                                 if (orig_msginfo->tags) {
11611                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11612                                         tmp_msginfo->folder->tags_dirty = TRUE;
11613                                 }
11614                         }
11615                 }
11616         }
11617
11618         if (!opening_multiple)
11619                 body = messageview_get_selection(msgview);
11620
11621         if (new_msglist) {
11622                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11623                 procmsg_msginfo_free(tmp_msginfo);
11624                 g_slist_free(new_msglist);
11625         } else
11626                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11627
11628         if (compose && originally_enc) {
11629                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11630         }
11631
11632         if (compose && originally_sig && compose->account->default_sign_reply) {
11633                 compose_force_signing(compose, compose->account, s_system);
11634         }
11635         g_free(s_system);
11636         g_free(body);
11637         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11638 }
11639
11640 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11641                                     guint action)
11642 {
11643         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11644         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11645                 GSList *cur = msginfo_list;
11646                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11647                                                "messages. Opening the windows "
11648                                                "could take some time. Do you "
11649                                                "want to continue?"), 
11650                                                g_slist_length(msginfo_list));
11651                 if (g_slist_length(msginfo_list) > 9
11652                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11653                     != G_ALERTALTERNATE) {
11654                         g_free(msg);
11655                         return;
11656                 }
11657                 g_free(msg);
11658                 /* We'll open multiple compose windows */
11659                 /* let the WM place the next windows */
11660                 compose_force_window_origin = FALSE;
11661                 for (; cur; cur = cur->next) {
11662                         GSList tmplist;
11663                         tmplist.data = cur->data;
11664                         tmplist.next = NULL;
11665                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11666                 }
11667                 compose_force_window_origin = TRUE;
11668         } else {
11669                 /* forwarding multiple mails as attachments is done via a
11670                  * single compose window */
11671                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11672         }
11673 }
11674
11675 void compose_check_for_email_account(Compose *compose)
11676 {
11677         PrefsAccount *ac = NULL, *curr = NULL;
11678         GList *list;
11679         
11680         if (!compose)
11681                 return;
11682
11683         if (compose->account && compose->account->protocol == A_NNTP) {
11684                 ac = account_get_cur_account();
11685                 if (ac->protocol == A_NNTP) {
11686                         list = account_get_list();
11687                         
11688                         for( ; list != NULL ; list = g_list_next(list)) {
11689                                 curr = (PrefsAccount *) list->data;
11690                                 if (curr->protocol != A_NNTP) {
11691                                         ac = curr;
11692                                         break;
11693                                 }
11694                         }
11695                 }
11696                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11697                                         ac->account_id); 
11698         }
11699 }
11700
11701 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11702                                 const gchar *address)
11703 {
11704         GSList *msginfo_list = NULL;
11705         gchar *body =  messageview_get_selection(msgview);
11706         Compose *compose;
11707         
11708         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11709         
11710         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11711         compose_check_for_email_account(compose);
11712         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11713         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11714         compose_reply_set_subject(compose, msginfo);
11715
11716         g_free(body);
11717         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11718 }
11719
11720 void compose_set_position(Compose *compose, gint pos)
11721 {
11722         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11723
11724         gtkut_text_view_set_position(text, pos);
11725 }
11726
11727 gboolean compose_search_string(Compose *compose,
11728                                 const gchar *str, gboolean case_sens)
11729 {
11730         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11731
11732         return gtkut_text_view_search_string(text, str, case_sens);
11733 }
11734
11735 gboolean compose_search_string_backward(Compose *compose,
11736                                 const gchar *str, gboolean case_sens)
11737 {
11738         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11739
11740         return gtkut_text_view_search_string_backward(text, str, case_sens);
11741 }
11742
11743 /* allocate a msginfo structure and populate its data from a compose data structure */
11744 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11745 {
11746         MsgInfo *newmsginfo;
11747         GSList *list;
11748         gchar buf[BUFFSIZE];
11749
11750         cm_return_val_if_fail( compose != NULL, NULL );
11751
11752         newmsginfo = procmsg_msginfo_new();
11753
11754         /* date is now */
11755         get_rfc822_date(buf, sizeof(buf));
11756         newmsginfo->date = g_strdup(buf);
11757
11758         /* from */
11759         if (compose->from_name) {
11760                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11761                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11762         }
11763
11764         /* subject */
11765         if (compose->subject_entry)
11766                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11767
11768         /* to, cc, reply-to, newsgroups */
11769         for (list = compose->header_list; list; list = list->next) {
11770                 gchar *header = gtk_editable_get_chars(
11771                                                                 GTK_EDITABLE(
11772                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11773                 gchar *entry = gtk_editable_get_chars(
11774                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11775
11776                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11777                         if ( newmsginfo->to == NULL ) {
11778                                 newmsginfo->to = g_strdup(entry);
11779                         } else if (entry && *entry) {
11780                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11781                                 g_free(newmsginfo->to);
11782                                 newmsginfo->to = tmp;
11783                         }
11784                 } else
11785                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11786                         if ( newmsginfo->cc == NULL ) {
11787                                 newmsginfo->cc = g_strdup(entry);
11788                         } else if (entry && *entry) {
11789                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11790                                 g_free(newmsginfo->cc);
11791                                 newmsginfo->cc = tmp;
11792                         }
11793                 } else
11794                 if ( strcasecmp(header,
11795                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11796                         if ( newmsginfo->newsgroups == NULL ) {
11797                                 newmsginfo->newsgroups = g_strdup(entry);
11798                         } else if (entry && *entry) {
11799                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11800                                 g_free(newmsginfo->newsgroups);
11801                                 newmsginfo->newsgroups = tmp;
11802                         }
11803                 }
11804
11805                 g_free(header);
11806                 g_free(entry);  
11807         }
11808
11809         /* other data is unset */
11810
11811         return newmsginfo;
11812 }
11813
11814 #ifdef USE_ENCHANT
11815 /* update compose's dictionaries from folder dict settings */
11816 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11817                                                 FolderItem *folder_item)
11818 {
11819         cm_return_if_fail(compose != NULL);
11820
11821         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11822                 FolderItemPrefs *prefs = folder_item->prefs;
11823
11824                 if (prefs->enable_default_dictionary)
11825                         gtkaspell_change_dict(compose->gtkaspell,
11826                                         prefs->default_dictionary, FALSE);
11827                 if (folder_item->prefs->enable_default_alt_dictionary)
11828                         gtkaspell_change_alt_dict(compose->gtkaspell,
11829                                         prefs->default_alt_dictionary);
11830                 if (prefs->enable_default_dictionary
11831                         || prefs->enable_default_alt_dictionary)
11832                         compose_spell_menu_changed(compose);
11833         }
11834 }
11835 #endif
11836
11837 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11838 {
11839         Compose *compose = (Compose *)data;
11840
11841         cm_return_if_fail(compose != NULL);
11842
11843         gtk_widget_grab_focus(compose->text);
11844 }
11845
11846 /*
11847  * End of Source.
11848  */