Properly allocate buffer in conv_encode_header_full() for quoted-printable encoding...
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #ifndef PANGO_ENABLE_ENGINE
28 #  define PANGO_ENABLE_ENGINE
29 #endif
30
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35
36 #include <pango/pango-break.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <time.h>
45 #include <stdlib.h>
46 #if HAVE_SYS_WAIT_H
47 #  include <sys/wait.h>
48 #endif
49 #include <signal.h>
50 #include <errno.h>
51 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
52 #include <libgen.h>
53 #endif
54
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
56 #  include <wchar.h>
57 #  include <wctype.h>
58 #endif
59
60 #include "claws.h"
61 #include "main.h"
62 #include "mainwindow.h"
63 #include "compose.h"
64 #ifndef USE_NEW_ADDRBOOK
65         #include "addressbook.h"
66 #else
67         #include "addressbook-dbus.h"
68         #include "addressadd.h"
69 #endif
70 #include "folderview.h"
71 #include "procmsg.h"
72 #include "menu.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
75 #include "imap.h"
76 #include "news.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
80 #include "action.h"
81 #include "account.h"
82 #include "filesel.h"
83 #include "procheader.h"
84 #include "procmime.h"
85 #include "statusbar.h"
86 #include "about.h"
87 #include "quoted-printable.h"
88 #include "codeconv.h"
89 #include "utils.h"
90 #include "gtkutils.h"
91 #include "gtkshruler.h"
92 #include "socket.h"
93 #include "alertpanel.h"
94 #include "manage_window.h"
95 #include "folder.h"
96 #include "folder_item_prefs.h"
97 #include "addr_compl.h"
98 #include "quote_fmt.h"
99 #include "undo.h"
100 #include "foldersel.h"
101 #include "toolbar.h"
102 #include "inc.h"
103 #include "message_search.h"
104 #include "combobox.h"
105 #include "hooks.h"
106 #include "privacy.h"
107 #include "timing.h"
108 #include "autofaces.h"
109 #include "spell_entry.h"
110
111 enum
112 {
113         COL_MIMETYPE = 0,
114         COL_SIZE     = 1,
115         COL_NAME     = 2,
116         COL_CHARSET  = 3,
117         COL_DATA     = 4,
118         COL_AUTODATA = 5,
119         N_COL_COLUMNS
120 };
121
122 #define N_ATTACH_COLS   (N_COL_COLUMNS)
123
124 typedef enum
125 {
126         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
127         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
128         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
129         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
130         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
131         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
132         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
133         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
134         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
135         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
136         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
137         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
138         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
139         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
140 } ComposeCallAdvancedAction;
141
142 typedef enum
143 {
144         PRIORITY_HIGHEST = 1,
145         PRIORITY_HIGH,
146         PRIORITY_NORMAL,
147         PRIORITY_LOW,
148         PRIORITY_LOWEST
149 } PriorityLevel;
150
151 typedef enum
152 {
153         COMPOSE_INSERT_SUCCESS,
154         COMPOSE_INSERT_READ_ERROR,
155         COMPOSE_INSERT_INVALID_CHARACTER,
156         COMPOSE_INSERT_NO_FILE
157 } ComposeInsertResult;
158
159 typedef enum
160 {
161         COMPOSE_WRITE_FOR_SEND,
162         COMPOSE_WRITE_FOR_STORE
163 } ComposeWriteType;
164
165 typedef enum
166 {
167         COMPOSE_QUOTE_FORCED,
168         COMPOSE_QUOTE_CHECK,
169         COMPOSE_QUOTE_SKIP
170 } ComposeQuoteMode;
171
172 typedef enum {
173     TO_FIELD_PRESENT,
174     SUBJECT_FIELD_PRESENT,
175     BODY_FIELD_PRESENT,
176     NO_FIELD_PRESENT
177 } MailField;
178
179 #define B64_LINE_SIZE           57
180 #define B64_BUFFSIZE            77
181
182 #define MAX_REFERENCES_LEN      999
183
184 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
185 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
186
187 static GList *compose_list = NULL;
188 static GSList *extra_headers = NULL;
189
190 static Compose *compose_generic_new                     (PrefsAccount   *account,
191                                                  const gchar    *to,
192                                                  FolderItem     *item,
193                                                  GList          *attach_files,
194                                                  GList          *listAddress );
195
196 static Compose *compose_create                  (PrefsAccount   *account,
197                                                  FolderItem              *item,
198                                                  ComposeMode     mode,
199                                                  gboolean batch);
200
201 static void compose_entry_mark_default_to       (Compose          *compose,
202                                          const gchar      *address);
203 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
204                                          ComposeQuoteMode        quote_mode,
205                                          gboolean        to_all,
206                                          gboolean        to_sender,
207                                          const gchar    *body);
208 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
209                                          GSList         *msginfo_list);
210 static Compose *compose_reply                   (MsgInfo        *msginfo,
211                                          ComposeQuoteMode        quote_mode,
212                                          gboolean        to_all,
213                                          gboolean        to_ml,
214                                          gboolean        to_sender,
215                                          const gchar    *body);
216 static Compose *compose_reply_mode              (ComposeMode     mode, 
217                                          GSList         *msginfo_list, 
218                                          gchar          *body);
219 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
220 static void compose_update_privacy_systems_menu(Compose *compose);
221
222 static GtkWidget *compose_account_option_menu_create
223                                                 (Compose        *compose);
224 static void compose_set_out_encoding            (Compose        *compose);
225 static void compose_set_template_menu           (Compose        *compose);
226 static void compose_destroy                     (Compose        *compose);
227
228 static MailField compose_entries_set            (Compose        *compose,
229                                                  const gchar    *mailto,
230                                                  ComposeEntryType to_type);
231 static gint compose_parse_header                (Compose        *compose,
232                                                  MsgInfo        *msginfo);
233 static gint compose_parse_manual_headers        (Compose        *compose,
234                                                  MsgInfo        *msginfo,
235                                                  HeaderEntry    *entries);
236 static gchar *compose_parse_references          (const gchar    *ref,
237                                                  const gchar    *msgid);
238
239 static gchar *compose_quote_fmt                 (Compose        *compose,
240                                                  MsgInfo        *msginfo,
241                                                  const gchar    *fmt,
242                                                  const gchar    *qmark,
243                                                  const gchar    *body,
244                                                  gboolean        rewrap,
245                                                  gboolean        need_unescape,
246                                                  const gchar *err_msg);
247
248 static void compose_reply_set_entry             (Compose        *compose,
249                                                  MsgInfo        *msginfo,
250                                                  gboolean        to_all,
251                                                  gboolean        to_ml,
252                                                  gboolean        to_sender,
253                                                  gboolean
254                                                  followup_and_reply_to);
255 static void compose_reedit_set_entry            (Compose        *compose,
256                                                  MsgInfo        *msginfo);
257
258 static void compose_insert_sig                  (Compose        *compose,
259                                                  gboolean        replace);
260 static ComposeInsertResult compose_insert_file  (Compose        *compose,
261                                                  const gchar    *file);
262
263 static gboolean compose_attach_append           (Compose        *compose,
264                                                  const gchar    *file,
265                                                  const gchar    *type,
266                                                  const gchar    *content_type,
267                                                  const gchar    *charset);
268 static void compose_attach_parts                (Compose        *compose,
269                                                  MsgInfo        *msginfo);
270
271 static gboolean compose_beautify_paragraph      (Compose        *compose,
272                                                  GtkTextIter    *par_iter,
273                                                  gboolean        force);
274 static void compose_wrap_all                    (Compose        *compose);
275 static void compose_wrap_all_full               (Compose        *compose,
276                                                  gboolean        autowrap);
277
278 static void compose_set_title                   (Compose        *compose);
279 static void compose_select_account              (Compose        *compose,
280                                                  PrefsAccount   *account,
281                                                  gboolean        init);
282
283 static PrefsAccount *compose_current_mail_account(void);
284 /* static gint compose_send                     (Compose        *compose); */
285 static gboolean compose_check_for_valid_recipient
286                                                 (Compose        *compose);
287 static gboolean compose_check_entries           (Compose        *compose,
288                                                  gboolean       check_everything);
289 static gint compose_write_to_file               (Compose        *compose,
290                                                  FILE           *fp,
291                                                  gint            action,
292                                                  gboolean        attach_parts);
293 static gint compose_write_body_to_file          (Compose        *compose,
294                                                  const gchar    *file);
295 static gint compose_remove_reedit_target        (Compose        *compose,
296                                                  gboolean        force);
297 static void compose_remove_draft                        (Compose        *compose);
298 static gint compose_queue_sub                   (Compose        *compose,
299                                                  gint           *msgnum,
300                                                  FolderItem     **item,
301                                                  gchar          **msgpath,
302                                                  gboolean       check_subject,
303                                                  gboolean       remove_reedit_target);
304 static int compose_add_attachments              (Compose        *compose,
305                                                  MimeInfo       *parent);
306 static gchar *compose_get_header                (Compose        *compose);
307 static gchar *compose_get_manual_headers_info   (Compose        *compose);
308
309 static void compose_convert_header              (Compose        *compose,
310                                                  gchar          *dest,
311                                                  gint            len,
312                                                  gchar          *src,
313                                                  gint            header_len,
314                                                  gboolean        addr_field);
315
316 static void compose_attach_info_free            (AttachInfo     *ainfo);
317 static void compose_attach_remove_selected      (GtkAction      *action,
318                                                  gpointer        data);
319
320 static void compose_template_apply              (Compose        *compose,
321                                                  Template       *tmpl,
322                                                  gboolean        replace);
323 static void compose_attach_property             (GtkAction      *action,
324                                                  gpointer        data);
325 static void compose_attach_property_create      (gboolean       *cancelled);
326 static void attach_property_ok                  (GtkWidget      *widget,
327                                                  gboolean       *cancelled);
328 static void attach_property_cancel              (GtkWidget      *widget,
329                                                  gboolean       *cancelled);
330 static gint attach_property_delete_event        (GtkWidget      *widget,
331                                                  GdkEventAny    *event,
332                                                  gboolean       *cancelled);
333 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
334                                                  GdkEventKey    *event,
335                                                  gboolean       *cancelled);
336
337 static void compose_exec_ext_editor             (Compose        *compose);
338 #ifdef G_OS_UNIX
339 static gint compose_exec_ext_editor_real        (const gchar    *file);
340 static gboolean compose_ext_editor_kill         (Compose        *compose);
341 static gboolean compose_input_cb                (GIOChannel     *source,
342                                                  GIOCondition    condition,
343                                                  gpointer        data);
344 static void compose_set_ext_editor_sensitive    (Compose        *compose,
345                                                  gboolean        sensitive);
346 #endif /* G_OS_UNIX */
347
348 static void compose_undo_state_changed          (UndoMain       *undostruct,
349                                                  gint            undo_state,
350                                                  gint            redo_state,
351                                                  gpointer        data);
352
353 static void compose_create_header_entry (Compose *compose);
354 static void compose_add_header_entry    (Compose *compose, const gchar *header,
355                                          gchar *text, ComposePrefType pref_type);
356 static void compose_remove_header_entries(Compose *compose);
357
358 static void compose_update_priority_menu_item(Compose * compose);
359 #if USE_ENCHANT
360 static void compose_spell_menu_changed  (void *data);
361 static void compose_dict_changed        (void *data);
362 #endif
363 static void compose_add_field_list      ( Compose *compose,
364                                           GList *listAddress );
365
366 /* callback functions */
367
368 static void compose_notebook_size_alloc (GtkNotebook *notebook,
369                                          GtkAllocation *allocation,
370                                          GtkPaned *paned);
371 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
372                                          GtkAllocation  *allocation,
373                                          GtkSHRuler     *shruler);
374 static void account_activated           (GtkComboBox *optmenu,
375                                          gpointer        data);
376 static void attach_selected             (GtkTreeView    *tree_view, 
377                                          GtkTreePath    *tree_path,
378                                          GtkTreeViewColumn *column, 
379                                          Compose *compose);
380 static gboolean attach_button_pressed   (GtkWidget      *widget,
381                                          GdkEventButton *event,
382                                          gpointer        data);
383 static gboolean attach_key_pressed      (GtkWidget      *widget,
384                                          GdkEventKey    *event,
385                                          gpointer        data);
386 static void compose_send_cb             (GtkAction      *action, gpointer data);
387 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
388
389 static void compose_save_cb             (GtkAction      *action,
390                                          gpointer        data);
391
392 static void compose_attach_cb           (GtkAction      *action,
393                                          gpointer        data);
394 static void compose_insert_file_cb      (GtkAction      *action,
395                                          gpointer        data);
396 static void compose_insert_sig_cb       (GtkAction      *action,
397                                          gpointer        data);
398 static void compose_replace_sig_cb      (GtkAction      *action,
399                                          gpointer        data);
400
401 static void compose_close_cb            (GtkAction      *action,
402                                          gpointer        data);
403 static void compose_print_cb            (GtkAction      *action,
404                                          gpointer        data);
405
406 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
407
408 static void compose_address_cb          (GtkAction      *action,
409                                          gpointer        data);
410 static void about_show_cb               (GtkAction      *action,
411                                          gpointer        data);
412 static void compose_template_activate_cb(GtkWidget      *widget,
413                                          gpointer        data);
414
415 static void compose_ext_editor_cb       (GtkAction      *action,
416                                          gpointer        data);
417
418 static gint compose_delete_cb           (GtkWidget      *widget,
419                                          GdkEventAny    *event,
420                                          gpointer        data);
421
422 static void compose_undo_cb             (GtkAction      *action,
423                                          gpointer        data);
424 static void compose_redo_cb             (GtkAction      *action,
425                                          gpointer        data);
426 static void compose_cut_cb              (GtkAction      *action,
427                                          gpointer        data);
428 static void compose_copy_cb             (GtkAction      *action,
429                                          gpointer        data);
430 static void compose_paste_cb            (GtkAction      *action,
431                                          gpointer        data);
432 static void compose_paste_as_quote_cb   (GtkAction      *action,
433                                          gpointer        data);
434 static void compose_paste_no_wrap_cb    (GtkAction      *action,
435                                          gpointer        data);
436 static void compose_paste_wrap_cb       (GtkAction      *action,
437                                          gpointer        data);
438 static void compose_allsel_cb           (GtkAction      *action,
439                                          gpointer        data);
440
441 static void compose_advanced_action_cb  (GtkAction      *action,
442                                          gpointer        data);
443
444 static void compose_grab_focus_cb       (GtkWidget      *widget,
445                                          Compose        *compose);
446
447 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
448                                          Compose        *compose);
449
450 static void compose_wrap_cb             (GtkAction      *action,
451                                          gpointer        data);
452 static void compose_wrap_all_cb         (GtkAction      *action,
453                                          gpointer        data);
454 static void compose_find_cb             (GtkAction      *action,
455                                          gpointer        data);
456 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
457                                          gpointer        data);
458 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
459                                          gpointer        data);
460
461 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
462                                          gpointer        data);
463 static void compose_toggle_sign_cb      (GtkToggleAction *action,
464                                          gpointer        data);
465 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
466                                          gpointer        data);
467 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
468 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
469 static void activate_privacy_system     (Compose *compose, 
470                                          PrefsAccount *account,
471                                          gboolean warn);
472 static void compose_use_signing(Compose *compose, gboolean use_signing);
473 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
474 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
475                                          gpointer        data);
476 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
477                                          gpointer        data);
478 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
479 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
480 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
481
482 static void compose_attach_drag_received_cb (GtkWidget          *widget,
483                                              GdkDragContext     *drag_context,
484                                              gint                x,
485                                              gint                y,
486                                              GtkSelectionData   *data,
487                                              guint               info,
488                                              guint               time,
489                                              gpointer            user_data);
490 static void compose_insert_drag_received_cb (GtkWidget          *widget,
491                                              GdkDragContext     *drag_context,
492                                              gint                x,
493                                              gint                y,
494                                              GtkSelectionData   *data,
495                                              guint               info,
496                                              guint               time,
497                                              gpointer            user_data);
498 static void compose_header_drag_received_cb (GtkWidget          *widget,
499                                              GdkDragContext     *drag_context,
500                                              gint                x,
501                                              gint                y,
502                                              GtkSelectionData   *data,
503                                              guint               info,
504                                              guint               time,
505                                              gpointer            user_data);
506
507 static gboolean compose_drag_drop           (GtkWidget *widget,
508                                              GdkDragContext *drag_context,
509                                              gint x, gint y,
510                                              guint time, gpointer user_data);
511 static gboolean completion_set_focus_to_subject
512                                         (GtkWidget    *widget,
513                                          GdkEventKey  *event,
514                                          Compose      *user_data);
515
516 static void text_inserted               (GtkTextBuffer  *buffer,
517                                          GtkTextIter    *iter,
518                                          const gchar    *text,
519                                          gint            len,
520                                          Compose        *compose);
521 static Compose *compose_generic_reply(MsgInfo *msginfo,
522                                   ComposeQuoteMode quote_mode,
523                                   gboolean to_all,
524                                   gboolean to_ml,
525                                   gboolean to_sender,
526                                   gboolean followup_and_reply_to,
527                                   const gchar *body);
528
529 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
530                                             ComposeHeaderEntry *headerentry);
531 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
532                                             GdkEventKey        *event,
533                                             ComposeHeaderEntry *headerentry);
534 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
535                                         ComposeHeaderEntry *headerentry);
536
537 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
538
539 static void compose_allow_user_actions (Compose *compose, gboolean allow);
540
541 static void compose_nothing_cb             (GtkAction *action, gpointer data)
542 {
543
544 }
545
546 #if USE_ENCHANT
547 static void compose_check_all              (GtkAction *action, gpointer data);
548 static void compose_highlight_all          (GtkAction *action, gpointer data);
549 static void compose_check_backwards        (GtkAction *action, gpointer data);
550 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
551 #endif
552
553 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
554
555 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
556
557 #ifdef USE_ENCHANT
558 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
559                                                 FolderItem *folder_item);
560 #endif
561 static void compose_attach_update_label(Compose *compose);
562 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
563                                      gboolean respect_default_to);
564 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
565 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
566
567 static GtkActionEntry compose_popup_entries[] =
568 {
569         {"Compose",                     NULL, "Compose" },
570         {"Compose/Add",                 NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
571         {"Compose/Remove",                      NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
572         {"Compose/---",                 NULL, "---", NULL, NULL, NULL },
573         {"Compose/Properties",          NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
574 };
575
576 static GtkActionEntry compose_entries[] =
577 {
578         {"Menu",                                NULL, "Menu" },
579 /* menus */
580         {"Message",                     NULL, N_("_Message") },
581         {"Edit",                        NULL, N_("_Edit") },
582 #if USE_ENCHANT
583         {"Spelling",                    NULL, N_("_Spelling") },
584 #endif
585         {"Options",                     NULL, N_("_Options") },
586         {"Tools",                       NULL, N_("_Tools") },
587         {"Help",                        NULL, N_("_Help") },
588 /* Message menu */
589         {"Message/Send",                NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
590         {"Message/SendLater",           NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
591         {"Message/---",                 NULL, "---" },
592
593         {"Message/AttachFile",          NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
594         {"Message/InsertFile",          NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
595         {"Message/InsertSig",           NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
596         {"Message/ReplaceSig",          NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
597         /* {"Message/---",              NULL, "---" }, */
598         {"Message/Save",                NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
599         /* {"Message/---",              NULL, "---" }, */
600         {"Message/Print",               NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
601         /* {"Message/---",              NULL, "---" }, */
602         {"Message/Close",               NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
603
604 /* Edit menu */
605         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
606         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
607         {"Edit/---",                    NULL, "---" },
608
609         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
610         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
611         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
612
613         {"Edit/SpecialPaste",           NULL, N_("_Special paste") },
614         {"Edit/SpecialPaste/AsQuotation",       NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
615         {"Edit/SpecialPaste/Wrapped",   NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
616         {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
617
618         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
619
620         {"Edit/Advanced",               NULL, N_("A_dvanced") },
621         {"Edit/Advanced/BackChar",      NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
622         {"Edit/Advanced/ForwChar",      NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
623         {"Edit/Advanced/BackWord",      NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
624         {"Edit/Advanced/ForwWord",      NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
625         {"Edit/Advanced/BegLine",       NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
626         {"Edit/Advanced/EndLine",       NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
627         {"Edit/Advanced/PrevLine",      NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
628         {"Edit/Advanced/NextLine",      NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
629         {"Edit/Advanced/DelBackChar",   NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
630         {"Edit/Advanced/DelForwChar",   NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
631         {"Edit/Advanced/DelBackWord",   NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
632         {"Edit/Advanced/DelForwWord",   NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
633         {"Edit/Advanced/DelLine",       NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
634         {"Edit/Advanced/DelEndLine",    NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
635
636         /* {"Edit/---",                 NULL, "---" }, */
637         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
638
639         /* {"Edit/---",                 NULL, "---" }, */
640         {"Edit/WrapPara",               NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
641         {"Edit/WrapAllLines",           NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
642         /* {"Edit/---",                 NULL, "---" }, */
643         {"Edit/ExtEditor",              NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
644 #if USE_ENCHANT
645 /* Spelling menu */
646         {"Spelling/CheckAllSel",        NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
647         {"Spelling/HighlightAll",       NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
648         {"Spelling/CheckBackwards",     NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
649         {"Spelling/ForwardNext",        NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
650
651         {"Spelling/---",                NULL, "---" },
652         {"Spelling/Options",            NULL, N_("_Options") },
653 #endif
654
655 /* Options menu */
656
657         {"Options/ReplyMode",           NULL, N_("Reply _mode") },
658         {"Options/---",                 NULL, "---" },
659         {"Options/PrivacySystem",       NULL, N_("Privacy _System") },
660         {"Options/PrivacySystem/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
661
662         /* {"Options/---",              NULL, "---" }, */
663
664         {"Options/Priority",            NULL, N_("_Priority") },
665
666         {"Options/Encoding",            NULL, N_("Character _encoding") },
667         {"Options/Encoding/---",        NULL, "---" },
668 #define ENC_ACTION(cs_char,c_char,string) \
669         { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
670
671         {"Options/Encoding/Western",    NULL, N_("Western European") },
672         {"Options/Encoding/Baltic",     NULL, N_("Baltic") },
673         {"Options/Encoding/Hebrew",     NULL, N_("Hebrew") },
674         {"Options/Encoding/Arabic",     NULL, N_("Arabic") },
675         {"Options/Encoding/Cyrillic",   NULL, N_("Cyrillic") },
676         {"Options/Encoding/Japanese",   NULL, N_("Japanese") },
677         {"Options/Encoding/Chinese",    NULL, N_("Chinese") },
678         {"Options/Encoding/Korean",     NULL, N_("Korean") },
679         {"Options/Encoding/Thai",       NULL, N_("Thai") },
680
681 /* Tools menu */
682         {"Tools/AddressBook",           NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
683
684         {"Tools/Template",      NULL, N_("_Template") },
685         {"Tools/Template/PlaceHolder",  NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
686         {"Tools/Actions",       NULL, N_("Actio_ns") },
687         {"Tools/Actions/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
688
689 /* Help menu */
690         {"Help/About",          NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
691 };
692
693 static GtkToggleActionEntry compose_toggle_entries[] =
694 {
695         {"Edit/AutoWrap",               NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
696         {"Edit/AutoIndent",             NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
697         {"Options/Sign",                NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
698         {"Options/Encrypt",             NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
699         {"Options/RequestRetRcpt",      NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
700         {"Options/RemoveReferences",    NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
701         {"Tools/ShowRuler",             NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
702 };
703
704 static GtkRadioActionEntry compose_radio_rm_entries[] =
705 {
706         {"Options/ReplyMode/Normal",    NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
707         {"Options/ReplyMode/All",       NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
708         {"Options/ReplyMode/Sender",    NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
709         {"Options/ReplyMode/List",      NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
710 };
711
712 static GtkRadioActionEntry compose_radio_prio_entries[] =
713 {
714         {"Options/Priority/Highest",    NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
715         {"Options/Priority/High",       NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
716         {"Options/Priority/Normal",     NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
717         {"Options/Priority/Low",        NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
718         {"Options/Priority/Lowest",     NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
719 };
720
721 static GtkRadioActionEntry compose_radio_enc_entries[] =
722 {
723         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
724         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
725         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
726         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
727         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
728         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
729         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
730         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
731         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
732         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
733         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
734         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
735         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
736         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
737         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
738         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
739         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
740         ENC_ACTION("Cyrillic/"CS_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         g_signal_connect_after(G_OBJECT(from_name), "activate",
8070                          G_CALLBACK(from_name_activate_cb), optmenu);
8071
8072         for (; accounts != NULL; accounts = accounts->next, num++) {
8073                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8074                 gchar *name, *from = NULL;
8075
8076                 if (ac == compose->account) def_menu = num;
8077
8078                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
8079                                        ac->account_name);
8080                 
8081                 if (ac == compose->account) {
8082                         if (ac->name && *ac->name) {
8083                                 gchar *buf;
8084                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8085                                 from = g_strdup_printf("%s <%s>",
8086                                                        buf, ac->address);
8087                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8088                         } else {
8089                                 from = g_strdup_printf("%s",
8090                                                        ac->address);
8091                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8092                         }
8093                 }
8094                 COMBOBOX_ADD(menu, name, ac->account_id);
8095                 g_free(name);
8096                 g_free(from);
8097         }
8098
8099         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8100
8101         g_signal_connect(G_OBJECT(optmenu), "changed",
8102                         G_CALLBACK(account_activated),
8103                         compose);
8104         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8105                          G_CALLBACK(compose_entry_popup_extend),
8106                          NULL);
8107
8108         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8109         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8110
8111         /* Putting only the GtkEntry into focus chain of parent hbox causes
8112          * the account selector combobox next to it to be unreachable when
8113          * navigating widgets in GtkTable with up/down arrow keys.
8114          * Note: gtk_widget_set_can_focus() was not enough. */
8115         GList *l = NULL;
8116         l = g_list_prepend(l, from_name);
8117         gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8118         g_list_free(l);
8119         
8120         CLAWS_SET_TIP(optmenubox,
8121                 _("Account to use for this email"));
8122         CLAWS_SET_TIP(from_name,
8123                 _("Sender address to be used"));
8124
8125         compose->account_combo = optmenu;
8126         compose->from_name = from_name;
8127         
8128         return hbox;
8129 }
8130
8131 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8132 {
8133         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8134         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8135         Compose *compose = (Compose *) data;
8136         if (active) {
8137                 compose->priority = value;
8138         }
8139 }
8140
8141 static void compose_reply_change_mode(Compose *compose,
8142                                     ComposeMode action)
8143 {
8144         gboolean was_modified = compose->modified;
8145
8146         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8147         
8148         cm_return_if_fail(compose->replyinfo != NULL);
8149         
8150         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8151                 ml = TRUE;
8152         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8153                 followup = TRUE;
8154         if (action == COMPOSE_REPLY_TO_ALL)
8155                 all = TRUE;
8156         if (action == COMPOSE_REPLY_TO_SENDER)
8157                 sender = TRUE;
8158         if (action == COMPOSE_REPLY_TO_LIST)
8159                 ml = TRUE;
8160
8161         compose_remove_header_entries(compose);
8162         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8163         if (compose->account->set_autocc && compose->account->auto_cc)
8164                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8165
8166         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8167                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8168         
8169         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8170                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8171         compose_show_first_last_header(compose, TRUE);
8172         compose->modified = was_modified;
8173         compose_set_title(compose);
8174 }
8175
8176 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8177 {
8178         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8179         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8180         Compose *compose = (Compose *) data;
8181         
8182         if (active)
8183                 compose_reply_change_mode(compose, value);
8184 }
8185
8186 static void compose_update_priority_menu_item(Compose * compose)
8187 {
8188         GtkWidget *menuitem = NULL;
8189         switch (compose->priority) {
8190                 case PRIORITY_HIGHEST:
8191                         menuitem = gtk_ui_manager_get_widget
8192                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8193                         break;
8194                 case PRIORITY_HIGH:
8195                         menuitem = gtk_ui_manager_get_widget
8196                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8197                         break;
8198                 case PRIORITY_NORMAL:
8199                         menuitem = gtk_ui_manager_get_widget
8200                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8201                         break;
8202                 case PRIORITY_LOW:
8203                         menuitem = gtk_ui_manager_get_widget
8204                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8205                         break;
8206                 case PRIORITY_LOWEST:
8207                         menuitem = gtk_ui_manager_get_widget
8208                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8209                         break;
8210         }
8211         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8212 }       
8213
8214 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8215 {
8216         Compose *compose = (Compose *) data;
8217         gchar *systemid;
8218         gboolean can_sign = FALSE, can_encrypt = FALSE;
8219
8220         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8221
8222         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8223                 return;
8224
8225         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8226         g_free(compose->privacy_system);
8227         compose->privacy_system = NULL;
8228         if (systemid != NULL) {
8229                 compose->privacy_system = g_strdup(systemid);
8230
8231                 can_sign = privacy_system_can_sign(systemid);
8232                 can_encrypt = privacy_system_can_encrypt(systemid);
8233         }
8234
8235         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8236
8237         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8238         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8239 }
8240
8241 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8242 {
8243         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8244         GtkWidget *menuitem = NULL;
8245         GList *children, *amenu;
8246         gboolean can_sign = FALSE, can_encrypt = FALSE;
8247         gboolean found = FALSE;
8248
8249         if (compose->privacy_system != NULL) {
8250                 gchar *systemid;
8251                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8252                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8253                 cm_return_if_fail(menuitem != NULL);
8254
8255                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8256                 amenu = children;
8257                 menuitem = NULL;
8258                 while (amenu != NULL) {
8259                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8260                         if (systemid != NULL) {
8261                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8262                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8263                                         menuitem = GTK_WIDGET(amenu->data);
8264
8265                                         can_sign = privacy_system_can_sign(systemid);
8266                                         can_encrypt = privacy_system_can_encrypt(systemid);
8267                                         found = TRUE;
8268                                         break;
8269                                 } 
8270                         } else if (strlen(compose->privacy_system) == 0 && 
8271                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8272                                         menuitem = GTK_WIDGET(amenu->data);
8273
8274                                         can_sign = FALSE;
8275                                         can_encrypt = FALSE;
8276                                         found = TRUE;
8277                                         break;
8278                         }
8279
8280                         amenu = amenu->next;
8281                 }
8282                 g_list_free(children);
8283                 if (menuitem != NULL)
8284                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8285                 
8286                 if (warn && !found && strlen(compose->privacy_system)) {
8287                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8288                                   "will not be able to sign or encrypt this message."),
8289                                   compose->privacy_system);
8290                 }
8291         } 
8292
8293         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8294         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8295 }       
8296  
8297 static void compose_set_out_encoding(Compose *compose)
8298 {
8299         CharSet out_encoding;
8300         const gchar *branch = NULL;
8301         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8302
8303         switch(out_encoding) {
8304                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8305                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8306                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8307                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8308                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8309                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8310                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8311                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8312                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8313                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8314                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8315                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8316                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8317                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8318                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8319                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8320                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8321                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8322                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8323                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8324                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8325                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8326                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8327                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8328                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8329                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8330                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8331                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8332                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8333                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8334                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8335                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8336                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8337         }
8338         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8339 }
8340
8341 static void compose_set_template_menu(Compose *compose)
8342 {
8343         GSList *tmpl_list, *cur;
8344         GtkWidget *menu;
8345         GtkWidget *item;
8346
8347         tmpl_list = template_get_config();
8348
8349         menu = gtk_menu_new();
8350
8351         gtk_menu_set_accel_group (GTK_MENU (menu), 
8352                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8353         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8354                 Template *tmpl = (Template *)cur->data;
8355                 gchar *accel_path = NULL;
8356                 item = gtk_menu_item_new_with_label(tmpl->name);
8357                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8358                 g_signal_connect(G_OBJECT(item), "activate",
8359                                  G_CALLBACK(compose_template_activate_cb),
8360                                  compose);
8361                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8362                 gtk_widget_show(item);
8363                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8364                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8365                 g_free(accel_path);
8366         }
8367
8368         gtk_widget_show(menu);
8369         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8370 }
8371
8372 void compose_update_actions_menu(Compose *compose)
8373 {
8374         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8375 }
8376
8377 static void compose_update_privacy_systems_menu(Compose *compose)
8378 {
8379         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8380         GSList *systems, *cur;
8381         GtkWidget *widget;
8382         GtkWidget *system_none;
8383         GSList *group;
8384         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8385         GtkWidget *privacy_menu = gtk_menu_new();
8386
8387         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8388         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8389
8390         g_signal_connect(G_OBJECT(system_none), "activate",
8391                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8392
8393         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8394         gtk_widget_show(system_none);
8395
8396         systems = privacy_get_system_ids();
8397         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8398                 gchar *systemid = cur->data;
8399
8400                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8401                 widget = gtk_radio_menu_item_new_with_label(group,
8402                         privacy_system_get_name(systemid));
8403                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8404                                        g_strdup(systemid), g_free);
8405                 g_signal_connect(G_OBJECT(widget), "activate",
8406                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8407
8408                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8409                 gtk_widget_show(widget);
8410                 g_free(systemid);
8411         }
8412         g_slist_free(systems);
8413         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8414         gtk_widget_show_all(privacy_menu);
8415         gtk_widget_show_all(privacy_menuitem);
8416 }
8417
8418 void compose_reflect_prefs_all(void)
8419 {
8420         GList *cur;
8421         Compose *compose;
8422
8423         for (cur = compose_list; cur != NULL; cur = cur->next) {
8424                 compose = (Compose *)cur->data;
8425                 compose_set_template_menu(compose);
8426         }
8427 }
8428
8429 void compose_reflect_prefs_pixmap_theme(void)
8430 {
8431         GList *cur;
8432         Compose *compose;
8433
8434         for (cur = compose_list; cur != NULL; cur = cur->next) {
8435                 compose = (Compose *)cur->data;
8436                 toolbar_update(TOOLBAR_COMPOSE, compose);
8437         }
8438 }
8439
8440 static const gchar *compose_quote_char_from_context(Compose *compose)
8441 {
8442         const gchar *qmark = NULL;
8443
8444         cm_return_val_if_fail(compose != NULL, NULL);
8445
8446         switch (compose->mode) {
8447                 /* use forward-specific quote char */
8448                 case COMPOSE_FORWARD:
8449                 case COMPOSE_FORWARD_AS_ATTACH:
8450                 case COMPOSE_FORWARD_INLINE:
8451                         if (compose->folder && compose->folder->prefs &&
8452                                         compose->folder->prefs->forward_with_format)
8453                                 qmark = compose->folder->prefs->forward_quotemark;
8454                         else if (compose->account->forward_with_format)
8455                                 qmark = compose->account->forward_quotemark;
8456                         else
8457                                 qmark = prefs_common.fw_quotemark;
8458                         break;
8459
8460                 /* use reply-specific quote char in all other modes */
8461                 default:
8462                         if (compose->folder && compose->folder->prefs &&
8463                                         compose->folder->prefs->reply_with_format)
8464                                 qmark = compose->folder->prefs->reply_quotemark;
8465                         else if (compose->account->reply_with_format)
8466                                 qmark = compose->account->reply_quotemark;
8467                         else
8468                                 qmark = prefs_common.quotemark;
8469                         break;
8470         }
8471
8472         if (qmark == NULL || *qmark == '\0')
8473                 qmark = "> ";
8474
8475         return qmark;
8476 }
8477
8478 static void compose_template_apply(Compose *compose, Template *tmpl,
8479                                    gboolean replace)
8480 {
8481         GtkTextView *text;
8482         GtkTextBuffer *buffer;
8483         GtkTextMark *mark;
8484         GtkTextIter iter;
8485         const gchar *qmark;
8486         gchar *parsed_str = NULL;
8487         gint cursor_pos = 0;
8488         const gchar *err_msg = _("The body of the template has an error at line %d.");
8489         if (!tmpl) return;
8490
8491         /* process the body */
8492
8493         text = GTK_TEXT_VIEW(compose->text);
8494         buffer = gtk_text_view_get_buffer(text);
8495
8496         if (tmpl->value) {
8497                 qmark = compose_quote_char_from_context(compose);
8498
8499                 if (compose->replyinfo != NULL) {
8500
8501                         if (replace)
8502                                 gtk_text_buffer_set_text(buffer, "", -1);
8503                         mark = gtk_text_buffer_get_insert(buffer);
8504                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8505
8506                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8507                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8508
8509                 } else if (compose->fwdinfo != NULL) {
8510
8511                         if (replace)
8512                                 gtk_text_buffer_set_text(buffer, "", -1);
8513                         mark = gtk_text_buffer_get_insert(buffer);
8514                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8515
8516                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8517                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8518
8519                 } else {
8520                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8521
8522                         GtkTextIter start, end;
8523                         gchar *tmp = NULL;
8524
8525                         gtk_text_buffer_get_start_iter(buffer, &start);
8526                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8527                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8528
8529                         /* clear the buffer now */
8530                         if (replace)
8531                                 gtk_text_buffer_set_text(buffer, "", -1);
8532
8533                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8534                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8535                         procmsg_msginfo_free( dummyinfo );
8536
8537                         g_free( tmp );
8538                 } 
8539         } else {
8540                 if (replace)
8541                         gtk_text_buffer_set_text(buffer, "", -1);
8542                 mark = gtk_text_buffer_get_insert(buffer);
8543                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8544         }       
8545
8546         if (replace && parsed_str && compose->account->auto_sig)
8547                 compose_insert_sig(compose, FALSE);
8548
8549         if (replace && parsed_str) {
8550                 gtk_text_buffer_get_start_iter(buffer, &iter);
8551                 gtk_text_buffer_place_cursor(buffer, &iter);
8552         }
8553         
8554         if (parsed_str) {
8555                 cursor_pos = quote_fmt_get_cursor_pos();
8556                 compose->set_cursor_pos = cursor_pos;
8557                 if (cursor_pos == -1)
8558                         cursor_pos = 0;
8559                 gtk_text_buffer_get_start_iter(buffer, &iter);
8560                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8561                 gtk_text_buffer_place_cursor(buffer, &iter);
8562         }
8563
8564         /* process the other fields */
8565
8566         compose_template_apply_fields(compose, tmpl);
8567         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8568         quote_fmt_reset_vartable();
8569         compose_changed_cb(NULL, compose);
8570
8571 #ifdef USE_ENCHANT
8572         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8573                 gtkaspell_highlight_all(compose->gtkaspell);
8574 #endif
8575 }
8576
8577 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8578 {
8579         MsgInfo* dummyinfo = NULL;
8580         MsgInfo *msginfo = NULL;
8581         gchar *buf = NULL;
8582
8583         if (compose->replyinfo != NULL)
8584                 msginfo = compose->replyinfo;
8585         else if (compose->fwdinfo != NULL)
8586                 msginfo = compose->fwdinfo;
8587         else {
8588                 dummyinfo = compose_msginfo_new_from_compose(compose);
8589                 msginfo = dummyinfo;
8590         }
8591
8592         if (tmpl->from && *tmpl->from != '\0') {
8593 #ifdef USE_ENCHANT
8594                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8595                                 compose->gtkaspell);
8596 #else
8597                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8598 #endif
8599                 quote_fmt_scan_string(tmpl->from);
8600                 quote_fmt_parse();
8601
8602                 buf = quote_fmt_get_buffer();
8603                 if (buf == NULL) {
8604                         alertpanel_error(_("Template From format error."));
8605                 } else {
8606                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8607                 }
8608         }
8609
8610         if (tmpl->to && *tmpl->to != '\0') {
8611 #ifdef USE_ENCHANT
8612                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8613                                 compose->gtkaspell);
8614 #else
8615                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8616 #endif
8617                 quote_fmt_scan_string(tmpl->to);
8618                 quote_fmt_parse();
8619
8620                 buf = quote_fmt_get_buffer();
8621                 if (buf == NULL) {
8622                         alertpanel_error(_("Template To format error."));
8623                 } else {
8624                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8625                 }
8626         }
8627
8628         if (tmpl->cc && *tmpl->cc != '\0') {
8629 #ifdef USE_ENCHANT
8630                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8631                                 compose->gtkaspell);
8632 #else
8633                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8634 #endif
8635                 quote_fmt_scan_string(tmpl->cc);
8636                 quote_fmt_parse();
8637
8638                 buf = quote_fmt_get_buffer();
8639                 if (buf == NULL) {
8640                         alertpanel_error(_("Template Cc format error."));
8641                 } else {
8642                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8643                 }
8644         }
8645
8646         if (tmpl->bcc && *tmpl->bcc != '\0') {
8647 #ifdef USE_ENCHANT
8648                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8649                                 compose->gtkaspell);
8650 #else
8651                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8652 #endif
8653                 quote_fmt_scan_string(tmpl->bcc);
8654                 quote_fmt_parse();
8655
8656                 buf = quote_fmt_get_buffer();
8657                 if (buf == NULL) {
8658                         alertpanel_error(_("Template Bcc format error."));
8659                 } else {
8660                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8661                 }
8662         }
8663
8664         if (tmpl->replyto && *tmpl->replyto != '\0') {
8665 #ifdef USE_ENCHANT
8666                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8667                                 compose->gtkaspell);
8668 #else
8669                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8670 #endif
8671                 quote_fmt_scan_string(tmpl->replyto);
8672                 quote_fmt_parse();
8673
8674                 buf = quote_fmt_get_buffer();
8675                 if (buf == NULL) {
8676                         alertpanel_error(_("Template Reply-To format error."));
8677                 } else {
8678                         compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
8679                 }
8680         }
8681
8682         /* process the subject */
8683         if (tmpl->subject && *tmpl->subject != '\0') {
8684 #ifdef USE_ENCHANT
8685                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8686                                 compose->gtkaspell);
8687 #else
8688                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8689 #endif
8690                 quote_fmt_scan_string(tmpl->subject);
8691                 quote_fmt_parse();
8692
8693                 buf = quote_fmt_get_buffer();
8694                 if (buf == NULL) {
8695                         alertpanel_error(_("Template subject format error."));
8696                 } else {
8697                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8698                 }
8699         }
8700
8701         procmsg_msginfo_free( dummyinfo );
8702 }
8703
8704 static void compose_destroy(Compose *compose)
8705 {
8706         GtkAllocation allocation;
8707         GtkTextBuffer *buffer;
8708         GtkClipboard *clipboard;
8709
8710         compose_list = g_list_remove(compose_list, compose);
8711
8712         if (compose->updating) {
8713                 debug_print("danger, not destroying anything now\n");
8714                 compose->deferred_destroy = TRUE;
8715                 return;
8716         }
8717
8718         /* NOTE: address_completion_end() does nothing with the window
8719          * however this may change. */
8720         address_completion_end(compose->window);
8721
8722         slist_free_strings_full(compose->to_list);
8723         slist_free_strings_full(compose->newsgroup_list);
8724         slist_free_strings_full(compose->header_list);
8725
8726         slist_free_strings_full(extra_headers);
8727         extra_headers = NULL;
8728
8729         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8730
8731         g_hash_table_destroy(compose->email_hashtable);
8732
8733         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8734                         compose->folder_update_callback_id);
8735
8736         procmsg_msginfo_free(compose->targetinfo);
8737         procmsg_msginfo_free(compose->replyinfo);
8738         procmsg_msginfo_free(compose->fwdinfo);
8739
8740         g_free(compose->replyto);
8741         g_free(compose->cc);
8742         g_free(compose->bcc);
8743         g_free(compose->newsgroups);
8744         g_free(compose->followup_to);
8745
8746         g_free(compose->ml_post);
8747
8748         g_free(compose->inreplyto);
8749         g_free(compose->references);
8750         g_free(compose->msgid);
8751         g_free(compose->boundary);
8752
8753         g_free(compose->redirect_filename);
8754         if (compose->undostruct)
8755                 undo_destroy(compose->undostruct);
8756
8757         g_free(compose->sig_str);
8758
8759         g_free(compose->exteditor_file);
8760
8761         g_free(compose->orig_charset);
8762
8763         g_free(compose->privacy_system);
8764
8765 #ifndef USE_NEW_ADDRBOOK
8766         if (addressbook_get_target_compose() == compose)
8767                 addressbook_set_target_compose(NULL);
8768 #endif
8769 #if USE_ENCHANT
8770         if (compose->gtkaspell) {
8771                 gtkaspell_delete(compose->gtkaspell);
8772                 compose->gtkaspell = NULL;
8773         }
8774 #endif
8775
8776         if (!compose->batch) {
8777                 gtk_widget_get_allocation(compose->window, &allocation);
8778                 prefs_common.compose_width = allocation.width;
8779                 prefs_common.compose_height = allocation.height;
8780         }
8781
8782         if (!gtk_widget_get_parent(compose->paned))
8783                 gtk_widget_destroy(compose->paned);
8784         gtk_widget_destroy(compose->popupmenu);
8785
8786         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8787         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8788         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8789
8790         gtk_widget_destroy(compose->window);
8791         toolbar_destroy(compose->toolbar);
8792         g_free(compose->toolbar);
8793         cm_mutex_free(compose->mutex);
8794         g_free(compose);
8795 }
8796
8797 static void compose_attach_info_free(AttachInfo *ainfo)
8798 {
8799         g_free(ainfo->file);
8800         g_free(ainfo->content_type);
8801         g_free(ainfo->name);
8802         g_free(ainfo->charset);
8803         g_free(ainfo);
8804 }
8805
8806 static void compose_attach_update_label(Compose *compose)
8807 {
8808         GtkTreeIter iter;
8809         gint i = 1;
8810         gchar *text;
8811         GtkTreeModel *model;
8812         
8813         if(compose == NULL)
8814                 return;
8815                 
8816         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8817         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8818                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8819                 return;
8820         }
8821         
8822         while(gtk_tree_model_iter_next(model, &iter))
8823                 i++;
8824         
8825         text = g_strdup_printf("(%d)", i);
8826         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8827         g_free(text);
8828 }
8829
8830 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8831 {
8832         Compose *compose = (Compose *)data;
8833         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8834         GtkTreeSelection *selection;
8835         GList *sel, *cur;
8836         GtkTreeModel *model;
8837
8838         selection = gtk_tree_view_get_selection(tree_view);
8839         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8840
8841         if (!sel) 
8842                 return;
8843
8844         for (cur = sel; cur != NULL; cur = cur->next) {
8845                 GtkTreePath *path = cur->data;
8846                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8847                                                 (model, cur->data);
8848                 cur->data = ref;
8849                 gtk_tree_path_free(path);
8850         }
8851
8852         for (cur = sel; cur != NULL; cur = cur->next) {
8853                 GtkTreeRowReference *ref = cur->data;
8854                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8855                 GtkTreeIter iter;
8856
8857                 if (gtk_tree_model_get_iter(model, &iter, path))
8858                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8859                 
8860                 gtk_tree_path_free(path);
8861                 gtk_tree_row_reference_free(ref);
8862         }
8863
8864         g_list_free(sel);
8865         compose_attach_update_label(compose);
8866 }
8867
8868 static struct _AttachProperty
8869 {
8870         GtkWidget *window;
8871         GtkWidget *mimetype_entry;
8872         GtkWidget *encoding_optmenu;
8873         GtkWidget *path_entry;
8874         GtkWidget *filename_entry;
8875         GtkWidget *ok_btn;
8876         GtkWidget *cancel_btn;
8877 } attach_prop;
8878
8879 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8880 {       
8881         gtk_tree_path_free((GtkTreePath *)ptr);
8882 }
8883
8884 static void compose_attach_property(GtkAction *action, gpointer data)
8885 {
8886         Compose *compose = (Compose *)data;
8887         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8888         AttachInfo *ainfo;
8889         GtkComboBox *optmenu;
8890         GtkTreeSelection *selection;
8891         GList *sel;
8892         GtkTreeModel *model;
8893         GtkTreeIter iter;
8894         GtkTreePath *path;
8895         static gboolean cancelled;
8896
8897         /* only if one selected */
8898         selection = gtk_tree_view_get_selection(tree_view);
8899         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8900                 return;
8901
8902         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8903         if (!sel)
8904                 return;
8905
8906         path = (GtkTreePath *) sel->data;
8907         gtk_tree_model_get_iter(model, &iter, path);
8908         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8909         
8910         if (!ainfo) {
8911                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8912                 g_list_free(sel);
8913                 return;
8914         }               
8915         g_list_free(sel);
8916
8917         if (!attach_prop.window)
8918                 compose_attach_property_create(&cancelled);
8919         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8920         gtk_widget_grab_focus(attach_prop.ok_btn);
8921         gtk_widget_show(attach_prop.window);
8922         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8923                         GTK_WINDOW(compose->window));
8924
8925         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8926         if (ainfo->encoding == ENC_UNKNOWN)
8927                 combobox_select_by_data(optmenu, ENC_BASE64);
8928         else
8929                 combobox_select_by_data(optmenu, ainfo->encoding);
8930
8931         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8932                            ainfo->content_type ? ainfo->content_type : "");
8933         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8934                            ainfo->file ? ainfo->file : "");
8935         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8936                            ainfo->name ? ainfo->name : "");
8937
8938         for (;;) {
8939                 const gchar *entry_text;
8940                 gchar *text;
8941                 gchar *cnttype = NULL;
8942                 gchar *file = NULL;
8943                 off_t size = 0;
8944
8945                 cancelled = FALSE;
8946                 gtk_main();
8947
8948                 gtk_widget_hide(attach_prop.window);
8949                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8950                 
8951                 if (cancelled)
8952                         break;
8953
8954                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8955                 if (*entry_text != '\0') {
8956                         gchar *p;
8957
8958                         text = g_strstrip(g_strdup(entry_text));
8959                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8960                                 cnttype = g_strdup(text);
8961                                 g_free(text);
8962                         } else {
8963                                 alertpanel_error(_("Invalid MIME type."));
8964                                 g_free(text);
8965                                 continue;
8966                         }
8967                 }
8968
8969                 ainfo->encoding = combobox_get_active_data(optmenu);
8970
8971                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8972                 if (*entry_text != '\0') {
8973                         if (is_file_exist(entry_text) &&
8974                             (size = get_file_size(entry_text)) > 0)
8975                                 file = g_strdup(entry_text);
8976                         else {
8977                                 alertpanel_error
8978                                         (_("File doesn't exist or is empty."));
8979                                 g_free(cnttype);
8980                                 continue;
8981                         }
8982                 }
8983
8984                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8985                 if (*entry_text != '\0') {
8986                         g_free(ainfo->name);
8987                         ainfo->name = g_strdup(entry_text);
8988                 }
8989
8990                 if (cnttype) {
8991                         g_free(ainfo->content_type);
8992                         ainfo->content_type = cnttype;
8993                 }
8994                 if (file) {
8995                         g_free(ainfo->file);
8996                         ainfo->file = file;
8997                 }
8998                 if (size)
8999                         ainfo->size = (goffset)size;
9000
9001                 /* update tree store */
9002                 text = to_human_readable(ainfo->size);
9003                 gtk_tree_model_get_iter(model, &iter, path);
9004                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
9005                                    COL_MIMETYPE, ainfo->content_type,
9006                                    COL_SIZE, text,
9007                                    COL_NAME, ainfo->name,
9008                                    COL_CHARSET, ainfo->charset,
9009                                    -1);
9010                 
9011                 break;
9012         }
9013
9014         gtk_tree_path_free(path);
9015 }
9016
9017 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9018 { \
9019         label = gtk_label_new(str); \
9020         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9021                          GTK_FILL, 0, 0, 0); \
9022         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9023  \
9024         entry = gtk_entry_new(); \
9025         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9026                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9027 }
9028
9029 static void compose_attach_property_create(gboolean *cancelled)
9030 {
9031         GtkWidget *window;
9032         GtkWidget *vbox;
9033         GtkWidget *table;
9034         GtkWidget *label;
9035         GtkWidget *mimetype_entry;
9036         GtkWidget *hbox;
9037         GtkWidget *optmenu;
9038         GtkListStore *optmenu_menu;
9039         GtkWidget *path_entry;
9040         GtkWidget *filename_entry;
9041         GtkWidget *hbbox;
9042         GtkWidget *ok_btn;
9043         GtkWidget *cancel_btn;
9044         GList     *mime_type_list, *strlist;
9045         GtkTreeIter iter;
9046
9047         debug_print("Creating attach_property window...\n");
9048
9049         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9050         gtk_widget_set_size_request(window, 480, -1);
9051         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9052         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9053         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9054         g_signal_connect(G_OBJECT(window), "delete_event",
9055                          G_CALLBACK(attach_property_delete_event),
9056                          cancelled);
9057         g_signal_connect(G_OBJECT(window), "key_press_event",
9058                          G_CALLBACK(attach_property_key_pressed),
9059                          cancelled);
9060
9061         vbox = gtk_vbox_new(FALSE, 8);
9062         gtk_container_add(GTK_CONTAINER(window), vbox);
9063
9064         table = gtk_table_new(4, 2, FALSE);
9065         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9066         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9067         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9068
9069         label = gtk_label_new(_("MIME type")); 
9070         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9071                          GTK_FILL, 0, 0, 0); 
9072         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9073 #if !GTK_CHECK_VERSION(2, 24, 0)
9074         mimetype_entry = gtk_combo_box_entry_new_text(); 
9075 #else
9076         mimetype_entry = gtk_combo_box_text_new_with_entry();
9077 #endif
9078         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9079                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9080                          
9081         /* stuff with list */
9082         mime_type_list = procmime_get_mime_type_list();
9083         strlist = NULL;
9084         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9085                 MimeType *type = (MimeType *) mime_type_list->data;
9086                 gchar *tmp;
9087
9088                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9089
9090                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9091                         g_free(tmp);
9092                 else
9093                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9094                                         (GCompareFunc)strcmp2);
9095         }
9096
9097         for (mime_type_list = strlist; mime_type_list != NULL; 
9098                 mime_type_list = mime_type_list->next) {
9099 #if !GTK_CHECK_VERSION(2, 24, 0)
9100                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9101 #else
9102                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9103 #endif
9104                 g_free(mime_type_list->data);
9105         }
9106         g_list_free(strlist);
9107         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9108         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9109
9110         label = gtk_label_new(_("Encoding"));
9111         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9112                          GTK_FILL, 0, 0, 0);
9113         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9114
9115         hbox = gtk_hbox_new(FALSE, 0);
9116         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9117                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9118
9119         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9120         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9121
9122         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9123         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9124         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9125         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9126         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9127
9128         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9129
9130         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9131         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9132
9133         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9134                                       &ok_btn, GTK_STOCK_OK,
9135                                       NULL, NULL);
9136         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9137         gtk_widget_grab_default(ok_btn);
9138
9139         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9140                          G_CALLBACK(attach_property_ok),
9141                          cancelled);
9142         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9143                          G_CALLBACK(attach_property_cancel),
9144                          cancelled);
9145
9146         gtk_widget_show_all(vbox);
9147
9148         attach_prop.window           = window;
9149         attach_prop.mimetype_entry   = mimetype_entry;
9150         attach_prop.encoding_optmenu = optmenu;
9151         attach_prop.path_entry       = path_entry;
9152         attach_prop.filename_entry   = filename_entry;
9153         attach_prop.ok_btn           = ok_btn;
9154         attach_prop.cancel_btn       = cancel_btn;
9155 }
9156
9157 #undef SET_LABEL_AND_ENTRY
9158
9159 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9160 {
9161         *cancelled = FALSE;
9162         gtk_main_quit();
9163 }
9164
9165 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9166 {
9167         *cancelled = TRUE;
9168         gtk_main_quit();
9169 }
9170
9171 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9172                                          gboolean *cancelled)
9173 {
9174         *cancelled = TRUE;
9175         gtk_main_quit();
9176
9177         return TRUE;
9178 }
9179
9180 static gboolean attach_property_key_pressed(GtkWidget *widget,
9181                                             GdkEventKey *event,
9182                                             gboolean *cancelled)
9183 {
9184         if (event && event->keyval == GDK_KEY_Escape) {
9185                 *cancelled = TRUE;
9186                 gtk_main_quit();
9187         }
9188         if (event && event->keyval == GDK_KEY_Return) {
9189                 *cancelled = FALSE;
9190                 gtk_main_quit();
9191                 return TRUE;
9192         }
9193         return FALSE;
9194 }
9195
9196 static void compose_exec_ext_editor(Compose *compose)
9197 {
9198 #ifdef G_OS_UNIX
9199         gchar *tmp;
9200         pid_t pid;
9201         gint pipe_fds[2];
9202
9203         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9204                               G_DIR_SEPARATOR, compose);
9205
9206         if (pipe(pipe_fds) < 0) {
9207                 perror("pipe");
9208                 g_free(tmp);
9209                 return;
9210         }
9211
9212         if ((pid = fork()) < 0) {
9213                 perror("fork");
9214                 g_free(tmp);
9215                 return;
9216         }
9217
9218         if (pid != 0) {
9219                 /* close the write side of the pipe */
9220                 close(pipe_fds[1]);
9221
9222                 compose->exteditor_file    = g_strdup(tmp);
9223                 compose->exteditor_pid     = pid;
9224
9225                 compose_set_ext_editor_sensitive(compose, FALSE);
9226
9227 #ifndef G_OS_WIN32
9228                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9229 #else
9230                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9231 #endif
9232                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9233                                                         G_IO_IN,
9234                                                         compose_input_cb,
9235                                                         compose);
9236         } else {        /* process-monitoring process */
9237                 pid_t pid_ed;
9238
9239                 if (setpgid(0, 0))
9240                         perror("setpgid");
9241
9242                 /* close the read side of the pipe */
9243                 close(pipe_fds[0]);
9244
9245                 if (compose_write_body_to_file(compose, tmp) < 0) {
9246                         fd_write_all(pipe_fds[1], "2\n", 2);
9247                         _exit(1);
9248                 }
9249
9250                 pid_ed = compose_exec_ext_editor_real(tmp);
9251                 if (pid_ed < 0) {
9252                         fd_write_all(pipe_fds[1], "1\n", 2);
9253                         _exit(1);
9254                 }
9255
9256                 /* wait until editor is terminated */
9257                 waitpid(pid_ed, NULL, 0);
9258
9259                 fd_write_all(pipe_fds[1], "0\n", 2);
9260
9261                 close(pipe_fds[1]);
9262                 _exit(0);
9263         }
9264
9265         g_free(tmp);
9266 #endif /* G_OS_UNIX */
9267 }
9268
9269 #ifdef G_OS_UNIX
9270 static gint compose_exec_ext_editor_real(const gchar *file)
9271 {
9272         gchar buf[1024];
9273         gchar *p;
9274         gchar **cmdline;
9275         pid_t pid;
9276
9277         cm_return_val_if_fail(file != NULL, -1);
9278
9279         if ((pid = fork()) < 0) {
9280                 perror("fork");
9281                 return -1;
9282         }
9283
9284         if (pid != 0) return pid;
9285
9286         /* grandchild process */
9287
9288         if (setpgid(0, getppid()))
9289                 perror("setpgid");
9290
9291         if (prefs_common_get_ext_editor_cmd() &&
9292             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9293             *(p + 1) == 's' && !strchr(p + 2, '%')) {
9294                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9295         } else {
9296                 if (prefs_common_get_ext_editor_cmd())
9297                         g_warning("External editor command-line is invalid: '%s'\n",
9298                                   prefs_common_get_ext_editor_cmd());
9299                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9300         }
9301
9302         cmdline = strsplit_with_quote(buf, " ", 1024);
9303         execvp(cmdline[0], cmdline);
9304
9305         perror("execvp");
9306         g_strfreev(cmdline);
9307
9308         _exit(1);
9309 }
9310
9311 static gboolean compose_ext_editor_kill(Compose *compose)
9312 {
9313         pid_t pgid = compose->exteditor_pid * -1;
9314         gint ret;
9315
9316         ret = kill(pgid, 0);
9317
9318         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9319                 AlertValue val;
9320                 gchar *msg;
9321
9322                 msg = g_strdup_printf
9323                         (_("The external editor is still working.\n"
9324                            "Force terminating the process?\n"
9325                            "process group id: %d"), -pgid);
9326                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9327                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9328                         
9329                 g_free(msg);
9330
9331                 if (val == G_ALERTALTERNATE) {
9332                         g_source_remove(compose->exteditor_tag);
9333                         g_io_channel_shutdown(compose->exteditor_ch,
9334                                               FALSE, NULL);
9335                         g_io_channel_unref(compose->exteditor_ch);
9336
9337                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9338                         waitpid(compose->exteditor_pid, NULL, 0);
9339
9340                         g_warning("Terminated process group id: %d", -pgid);
9341                         g_warning("Temporary file: %s",
9342                                   compose->exteditor_file);
9343
9344                         compose_set_ext_editor_sensitive(compose, TRUE);
9345
9346                         g_free(compose->exteditor_file);
9347                         compose->exteditor_file    = NULL;
9348                         compose->exteditor_pid     = -1;
9349                         compose->exteditor_ch      = NULL;
9350                         compose->exteditor_tag     = -1;
9351                 } else
9352                         return FALSE;
9353         }
9354
9355         return TRUE;
9356 }
9357
9358 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9359                                  gpointer data)
9360 {
9361         gchar buf[3] = "3";
9362         Compose *compose = (Compose *)data;
9363         gsize bytes_read;
9364
9365         debug_print("Compose: input from monitoring process\n");
9366
9367         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9368
9369         g_io_channel_shutdown(source, FALSE, NULL);
9370         g_io_channel_unref(source);
9371
9372         waitpid(compose->exteditor_pid, NULL, 0);
9373
9374         if (buf[0] == '0') {            /* success */
9375                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9376                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9377
9378                 gtk_text_buffer_set_text(buffer, "", -1);
9379                 compose_insert_file(compose, compose->exteditor_file);
9380                 compose_changed_cb(NULL, compose);
9381                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9382
9383                 if (claws_unlink(compose->exteditor_file) < 0)
9384                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9385         } else if (buf[0] == '1') {     /* failed */
9386                 g_warning("Couldn't exec external editor\n");
9387                 if (claws_unlink(compose->exteditor_file) < 0)
9388                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9389         } else if (buf[0] == '2') {
9390                 g_warning("Couldn't write to file\n");
9391         } else if (buf[0] == '3') {
9392                 g_warning("Pipe read failed\n");
9393         }
9394
9395         compose_set_ext_editor_sensitive(compose, TRUE);
9396
9397         g_free(compose->exteditor_file);
9398         compose->exteditor_file    = NULL;
9399         compose->exteditor_pid     = -1;
9400         compose->exteditor_ch      = NULL;
9401         compose->exteditor_tag     = -1;
9402
9403         return FALSE;
9404 }
9405
9406 static void compose_set_ext_editor_sensitive(Compose *compose,
9407                                              gboolean sensitive)
9408 {
9409         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9410         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9411         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9412         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9413         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", sensitive);
9414         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9415         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9416         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9417
9418         gtk_widget_set_sensitive(compose->text,                       sensitive);
9419         if (compose->toolbar->send_btn)
9420                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9421         if (compose->toolbar->sendl_btn)
9422                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9423         if (compose->toolbar->draft_btn)
9424                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9425         if (compose->toolbar->insert_btn)
9426                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9427         if (compose->toolbar->sig_btn)
9428                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9429         if (compose->toolbar->exteditor_btn)
9430                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9431         if (compose->toolbar->linewrap_current_btn)
9432                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9433         if (compose->toolbar->linewrap_all_btn)
9434                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9435 }
9436 #endif /* G_OS_UNIX */
9437
9438 /**
9439  * compose_undo_state_changed:
9440  *
9441  * Change the sensivity of the menuentries undo and redo
9442  **/
9443 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9444                                        gint redo_state, gpointer data)
9445 {
9446         Compose *compose = (Compose *)data;
9447
9448         switch (undo_state) {
9449         case UNDO_STATE_TRUE:
9450                 if (!undostruct->undo_state) {
9451                         undostruct->undo_state = TRUE;
9452                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9453                 }
9454                 break;
9455         case UNDO_STATE_FALSE:
9456                 if (undostruct->undo_state) {
9457                         undostruct->undo_state = FALSE;
9458                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9459                 }
9460                 break;
9461         case UNDO_STATE_UNCHANGED:
9462                 break;
9463         case UNDO_STATE_REFRESH:
9464                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9465                 break;
9466         default:
9467                 g_warning("Undo state not recognized");
9468                 break;
9469         }
9470
9471         switch (redo_state) {
9472         case UNDO_STATE_TRUE:
9473                 if (!undostruct->redo_state) {
9474                         undostruct->redo_state = TRUE;
9475                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9476                 }
9477                 break;
9478         case UNDO_STATE_FALSE:
9479                 if (undostruct->redo_state) {
9480                         undostruct->redo_state = FALSE;
9481                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9482                 }
9483                 break;
9484         case UNDO_STATE_UNCHANGED:
9485                 break;
9486         case UNDO_STATE_REFRESH:
9487                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9488                 break;
9489         default:
9490                 g_warning("Redo state not recognized");
9491                 break;
9492         }
9493 }
9494
9495 /* callback functions */
9496
9497 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9498                                         GtkAllocation *allocation,
9499                                         GtkPaned *paned)
9500 {
9501         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9502 }
9503
9504 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9505  * includes "non-client" (windows-izm) in calculation, so this calculation
9506  * may not be accurate.
9507  */
9508 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9509                                         GtkAllocation *allocation,
9510                                         GtkSHRuler *shruler)
9511 {
9512         if (prefs_common.show_ruler) {
9513                 gint char_width = 0, char_height = 0;
9514                 gint line_width_in_chars;
9515
9516                 gtkut_get_font_size(GTK_WIDGET(widget),
9517                                     &char_width, &char_height);
9518                 line_width_in_chars =
9519                         (allocation->width - allocation->x) / char_width;
9520
9521                 /* got the maximum */
9522                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9523                                     0.0, line_width_in_chars, 0);
9524         }
9525
9526         return TRUE;
9527 }
9528
9529 typedef struct {
9530         gchar                   *header;
9531         gchar                   *entry;
9532         ComposePrefType         type;
9533         gboolean                entry_marked;
9534 } HeaderEntryState;
9535
9536 static void account_activated(GtkComboBox *optmenu, gpointer data)
9537 {
9538         Compose *compose = (Compose *)data;
9539
9540         PrefsAccount *ac;
9541         gchar *folderidentifier;
9542         gint account_id = 0;
9543         GtkTreeModel *menu;
9544         GtkTreeIter iter;
9545         GSList *list, *saved_list = NULL;
9546         HeaderEntryState *state;
9547         GtkRcStyle *style = NULL;
9548 #if !GTK_CHECK_VERSION(3, 0, 0)
9549         static GdkColor yellow;
9550         static gboolean color_set = FALSE;
9551 #else
9552         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9553 #endif
9554
9555         /* Get ID of active account in the combo box */
9556         menu = gtk_combo_box_get_model(optmenu);
9557         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9558         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9559
9560         ac = account_find_from_id(account_id);
9561         cm_return_if_fail(ac != NULL);
9562
9563         if (ac != compose->account) {
9564                 compose_select_account(compose, ac, FALSE);
9565
9566                 for (list = compose->header_list; list; list = list->next) {
9567                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9568                         
9569                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9570                                 compose_destroy_headerentry(compose, hentry);
9571                                 continue;
9572                         }
9573                         
9574                         state = g_malloc0(sizeof(HeaderEntryState));
9575                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9576                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9577                         state->entry = gtk_editable_get_chars(
9578                                         GTK_EDITABLE(hentry->entry), 0, -1);
9579                         state->type = hentry->type;
9580                                 
9581 #if !GTK_CHECK_VERSION(3, 0, 0)
9582                         if (!color_set) {
9583                                 gdk_color_parse("#f5f6be", &yellow);
9584                                 color_set = gdk_colormap_alloc_color(
9585                                                         gdk_colormap_get_system(),
9586                                                         &yellow, FALSE, TRUE);
9587                         }
9588 #endif
9589                                 
9590                         style = gtk_widget_get_modifier_style(hentry->entry);
9591                         state->entry_marked = gdk_color_equal(&yellow,
9592                                                 &style->base[GTK_STATE_NORMAL]);
9593
9594                         saved_list = g_slist_append(saved_list, state);
9595                         compose_destroy_headerentry(compose, hentry);
9596                 }
9597
9598                 compose->header_last = NULL;
9599                 g_slist_free(compose->header_list);
9600                 compose->header_list = NULL;
9601                 compose->header_nextrow = 1;
9602                 compose_create_header_entry(compose);
9603                 
9604                 if (ac->set_autocc && ac->auto_cc)
9605                         compose_entry_append(compose, ac->auto_cc,
9606                                                 COMPOSE_CC, PREF_ACCOUNT);
9607
9608                 if (ac->set_autobcc && ac->auto_bcc) 
9609                         compose_entry_append(compose, ac->auto_bcc,
9610                                                 COMPOSE_BCC, PREF_ACCOUNT);
9611         
9612                 if (ac->set_autoreplyto && ac->auto_replyto)
9613                         compose_entry_append(compose, ac->auto_replyto,
9614                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9615                 
9616                 for (list = saved_list; list; list = list->next) {
9617                         state = (HeaderEntryState *) list->data;
9618                         
9619                         compose_add_header_entry(compose, state->header,
9620                                                 state->entry, state->type);
9621                         if (state->entry_marked)
9622                                 compose_entry_mark_default_to(compose, state->entry);
9623                         
9624                         g_free(state->header);  
9625                         g_free(state->entry);
9626                         g_free(state);
9627                 }
9628                 g_slist_free(saved_list);
9629                 
9630                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9631                                         (ac->protocol == A_NNTP) ? 
9632                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9633         }
9634
9635         /* Set message save folder */
9636         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9637                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9638         }
9639         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9640                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9641                            
9642         compose_set_save_to(compose, NULL);
9643         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9644                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9645                                   (compose->account, F_OUTBOX));
9646                 compose_set_save_to(compose, folderidentifier);
9647                 g_free(folderidentifier);
9648         }
9649 }
9650
9651 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9652                             GtkTreeViewColumn *column, Compose *compose)
9653 {
9654         compose_attach_property(NULL, compose);
9655 }
9656
9657 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9658                                       gpointer data)
9659 {
9660         Compose *compose = (Compose *)data;
9661         GtkTreeSelection *attach_selection;
9662         gint attach_nr_selected;
9663         
9664         if (!event) return FALSE;
9665
9666         if (event->button == 3) {
9667                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9668                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9669                         
9670                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9671                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9672                         
9673                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9674                                NULL, NULL, event->button, event->time);
9675                 return TRUE;                           
9676         }
9677
9678         return FALSE;
9679 }
9680
9681 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9682                                    gpointer data)
9683 {
9684         Compose *compose = (Compose *)data;
9685
9686         if (!event) return FALSE;
9687
9688         switch (event->keyval) {
9689         case GDK_KEY_Delete:
9690                 compose_attach_remove_selected(NULL, compose);
9691                 break;
9692         }
9693         return FALSE;
9694 }
9695
9696 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9697 {
9698         toolbar_comp_set_sensitive(compose, allow);
9699         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9700         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9701 #if USE_ENCHANT
9702         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9703 #endif  
9704         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9705         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9706         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9707         
9708         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9709
9710 }
9711
9712 static void compose_send_cb(GtkAction *action, gpointer data)
9713 {
9714         Compose *compose = (Compose *)data;
9715
9716         if (prefs_common.work_offline && 
9717             !inc_offline_should_override(TRUE,
9718                 _("Claws Mail needs network access in order "
9719                   "to send this email.")))
9720                 return;
9721         
9722         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9723                 g_source_remove(compose->draft_timeout_tag);
9724                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
9725         }
9726
9727         compose_send(compose);
9728 }
9729
9730 static void compose_send_later_cb(GtkAction *action, gpointer data)
9731 {
9732         Compose *compose = (Compose *)data;
9733         gint val;
9734
9735         inc_lock();
9736         compose_allow_user_actions(compose, FALSE);
9737         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9738         compose_allow_user_actions(compose, TRUE);
9739         inc_unlock();
9740
9741         if (!val) {
9742                 compose_close(compose);
9743         } else if (val == -1) {
9744                 alertpanel_error(_("Could not queue message."));
9745         } else if (val == -2) {
9746                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9747         } else if (val == -3) {
9748                 if (privacy_peek_error())
9749                 alertpanel_error(_("Could not queue message for sending:\n\n"
9750                                    "Signature failed: %s"), privacy_get_error());
9751         } else if (val == -4) {
9752                 alertpanel_error(_("Could not queue message for sending:\n\n"
9753                                    "Charset conversion failed."));
9754         } else if (val == -5) {
9755                 alertpanel_error(_("Could not queue message for sending:\n\n"
9756                                    "Couldn't get recipient encryption key."));
9757         } else if (val == -6) {
9758                 /* silent error */
9759         }
9760         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9761 }
9762
9763 #define DRAFTED_AT_EXIT "drafted_at_exit"
9764 static void compose_register_draft(MsgInfo *info)
9765 {
9766         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9767                                       DRAFTED_AT_EXIT, NULL);
9768         FILE *fp = g_fopen(filepath, "ab");
9769         
9770         if (fp) {
9771                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9772                                 info->msgnum);
9773                 fclose(fp);
9774         }
9775                 
9776         g_free(filepath);       
9777 }
9778
9779 gboolean compose_draft (gpointer data, guint action) 
9780 {
9781         Compose *compose = (Compose *)data;
9782         FolderItem *draft;
9783         gchar *tmp;
9784         gchar *sheaders;
9785         gint msgnum;
9786         MsgFlags flag = {0, 0};
9787         static gboolean lock = FALSE;
9788         MsgInfo *newmsginfo;
9789         FILE *fp;
9790         gboolean target_locked = FALSE;
9791         gboolean err = FALSE;
9792
9793         if (lock) return FALSE;
9794
9795         if (compose->sending)
9796                 return TRUE;
9797
9798         draft = account_get_special_folder(compose->account, F_DRAFT);
9799         cm_return_val_if_fail(draft != NULL, FALSE);
9800         
9801         if (!g_mutex_trylock(compose->mutex)) {
9802                 /* we don't want to lock the mutex once it's available,
9803                  * because as the only other part of compose.c locking
9804                  * it is compose_close - which means once unlocked,
9805                  * the compose struct will be freed */
9806                 debug_print("couldn't lock mutex, probably sending\n");
9807                 return FALSE;
9808         }
9809
9810         lock = TRUE;
9811
9812         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9813                               G_DIR_SEPARATOR, compose);
9814         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9815                 FILE_OP_ERROR(tmp, "fopen");
9816                 goto warn_err;
9817         }
9818
9819         /* chmod for security */
9820         if (change_file_mode_rw(fp, tmp) < 0) {
9821                 FILE_OP_ERROR(tmp, "chmod");
9822                 g_warning("can't change file mode\n");
9823         }
9824
9825         /* Save draft infos */
9826         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9827         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9828
9829         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9830                 gchar *savefolderid;
9831
9832                 savefolderid = compose_get_save_to(compose);
9833                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9834                 g_free(savefolderid);
9835         }
9836         if (compose->return_receipt) {
9837                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9838         }
9839         if (compose->privacy_system) {
9840                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9841                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9842                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9843         }
9844
9845         /* Message-ID of message replying to */
9846         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9847                 gchar *folderid = NULL;
9848
9849                 if (compose->replyinfo->folder)
9850                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
9851                 if (folderid == NULL)
9852                         folderid = g_strdup("NULL");
9853
9854                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9855                 g_free(folderid);
9856         }
9857         /* Message-ID of message forwarding to */
9858         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9859                 gchar *folderid = NULL;
9860
9861                 if (compose->fwdinfo->folder)
9862                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9863                 if (folderid == NULL)
9864                         folderid = g_strdup("NULL");
9865
9866                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9867                 g_free(folderid);
9868         }
9869
9870         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9871         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9872
9873         sheaders = compose_get_manual_headers_info(compose);
9874         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9875         g_free(sheaders);
9876
9877         /* end of headers */
9878         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9879
9880         if (err) {
9881                 fclose(fp);
9882                 goto warn_err;
9883         }
9884
9885         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9886                 fclose(fp);
9887                 goto warn_err;
9888         }
9889         if (fclose(fp) == EOF) {
9890                 goto warn_err;
9891         }
9892         
9893         flag.perm_flags = MSG_NEW|MSG_UNREAD;
9894         if (compose->targetinfo) {
9895                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9896                 if (target_locked) 
9897                         flag.perm_flags |= MSG_LOCKED;
9898         }
9899         flag.tmp_flags = MSG_DRAFT;
9900
9901         folder_item_scan(draft);
9902         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9903                 MsgInfo *tmpinfo = NULL;
9904                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9905                 if (compose->msgid) {
9906                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9907                 }
9908                 if (tmpinfo) {
9909                         msgnum = tmpinfo->msgnum;
9910                         procmsg_msginfo_free(tmpinfo);
9911                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9912                 } else {
9913                         debug_print("didn't get draft msgnum after scanning\n");
9914                 }
9915         } else {
9916                 debug_print("got draft msgnum %d from adding\n", msgnum);
9917         }
9918         if (msgnum < 0) {
9919 warn_err:
9920                 claws_unlink(tmp);
9921                 g_free(tmp);
9922                 if (action != COMPOSE_AUTO_SAVE) {
9923                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9924                                 alertpanel_error(_("Could not save draft."));
9925                         else {
9926                                 AlertValue val;
9927                                 gtkut_window_popup(compose->window);
9928                                 val = alertpanel_full(_("Could not save draft"),
9929                                         _("Could not save draft.\n"
9930                                         "Do you want to cancel exit or discard this email?"),
9931                                           _("_Cancel exit"), _("_Discard email"), NULL,
9932                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9933                                 if (val == G_ALERTALTERNATE) {
9934                                         lock = FALSE;
9935                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9936                                         compose_close(compose);
9937                                         return TRUE;
9938                                 } else {
9939                                         lock = FALSE;
9940                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9941                                         return FALSE;
9942                                 }
9943                         }
9944                 }
9945                 goto unlock;
9946         }
9947         g_free(tmp);
9948
9949         if (compose->mode == COMPOSE_REEDIT) {
9950                 compose_remove_reedit_target(compose, TRUE);
9951         }
9952
9953         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9954
9955         if (newmsginfo) {
9956                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9957                 if (target_locked)
9958                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
9959                 else
9960                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
9961                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9962                         procmsg_msginfo_set_flags(newmsginfo, 0,
9963                                                   MSG_HAS_ATTACHMENT);
9964
9965                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9966                         compose_register_draft(newmsginfo);
9967                 }
9968                 procmsg_msginfo_free(newmsginfo);
9969         }
9970         
9971         folder_item_scan(draft);
9972         
9973         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9974                 lock = FALSE;
9975                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9976                 compose_close(compose);
9977                 return TRUE;
9978         } else {
9979                 struct stat s;
9980                 gchar *path;
9981
9982                 path = folder_item_fetch_msg(draft, msgnum);
9983                 if (path == NULL) {
9984                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9985                         goto unlock;
9986                 }
9987                 if (g_stat(path, &s) < 0) {
9988                         FILE_OP_ERROR(path, "stat");
9989                         g_free(path);
9990                         goto unlock;
9991                 }
9992                 g_free(path);
9993
9994                 procmsg_msginfo_free(compose->targetinfo);
9995                 compose->targetinfo = procmsg_msginfo_new();
9996                 compose->targetinfo->msgnum = msgnum;
9997                 compose->targetinfo->size = (goffset)s.st_size;
9998                 compose->targetinfo->mtime = s.st_mtime;
9999                 compose->targetinfo->folder = draft;
10000                 if (target_locked)
10001                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
10002                 compose->mode = COMPOSE_REEDIT;
10003                 
10004                 if (action == COMPOSE_AUTO_SAVE) {
10005                         compose->autosaved_draft = compose->targetinfo;
10006                 }
10007                 compose->modified = FALSE;
10008                 compose_set_title(compose);
10009         }
10010 unlock:
10011         lock = FALSE;
10012         g_mutex_unlock(compose->mutex);
10013         return TRUE;
10014 }
10015
10016 void compose_clear_exit_drafts(void)
10017 {
10018         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10019                                       DRAFTED_AT_EXIT, NULL);
10020         if (is_file_exist(filepath))
10021                 claws_unlink(filepath);
10022         
10023         g_free(filepath);
10024 }
10025
10026 void compose_reopen_exit_drafts(void)
10027 {
10028         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10029                                       DRAFTED_AT_EXIT, NULL);
10030         FILE *fp = g_fopen(filepath, "rb");
10031         gchar buf[1024];
10032         
10033         if (fp) {
10034                 while (fgets(buf, sizeof(buf), fp)) {
10035                         gchar **parts = g_strsplit(buf, "\t", 2);
10036                         const gchar *folder = parts[0];
10037                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10038                         
10039                         if (folder && *folder && msgnum > -1) {
10040                                 FolderItem *item = folder_find_item_from_identifier(folder);
10041                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10042                                 if (info)
10043                                         compose_reedit(info, FALSE);
10044                         }
10045                         g_strfreev(parts);
10046                 }       
10047                 fclose(fp);
10048         }       
10049         g_free(filepath);
10050         compose_clear_exit_drafts();
10051 }
10052
10053 static void compose_save_cb(GtkAction *action, gpointer data)
10054 {
10055         Compose *compose = (Compose *)data;
10056         compose_draft(compose, COMPOSE_KEEP_EDITING);
10057         compose->rmode = COMPOSE_REEDIT;
10058 }
10059
10060 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10061 {
10062         if (compose && file_list) {
10063                 GList *tmp;
10064
10065                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10066                         gchar *file = (gchar *) tmp->data;
10067                         gchar *utf8_filename = conv_filename_to_utf8(file);
10068                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10069                         compose_changed_cb(NULL, compose);
10070                         if (free_data) {
10071                         g_free(file);
10072                                 tmp->data = NULL;
10073                         }
10074                         g_free(utf8_filename);
10075                 }
10076         }
10077 }
10078
10079 static void compose_attach_cb(GtkAction *action, gpointer data)
10080 {
10081         Compose *compose = (Compose *)data;
10082         GList *file_list;
10083
10084         if (compose->redirect_filename != NULL)
10085                 return;
10086
10087         /* Set focus_window properly, in case we were called via popup menu,
10088          * which unsets it (via focus_out_event callback on compose window). */
10089         manage_window_focus_in(compose->window, NULL, NULL);
10090
10091         file_list = filesel_select_multiple_files_open(_("Select file"));
10092
10093         if (file_list) {
10094                 compose_attach_from_list(compose, file_list, TRUE);
10095                 g_list_free(file_list);
10096         }
10097 }
10098
10099 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10100 {
10101         Compose *compose = (Compose *)data;
10102         GList *file_list;
10103         gint files_inserted = 0;
10104
10105         file_list = filesel_select_multiple_files_open(_("Select file"));
10106
10107         if (file_list) {
10108                 GList *tmp;
10109
10110                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10111                         gchar *file = (gchar *) tmp->data;
10112                         gchar *filedup = g_strdup(file);
10113                         gchar *shortfile = g_path_get_basename(filedup);
10114                         ComposeInsertResult res;
10115                         /* insert the file if the file is short or if the user confirmed that
10116                            he/she wants to insert the large file */
10117                         res = compose_insert_file(compose, file);
10118                         if (res == COMPOSE_INSERT_READ_ERROR) {
10119                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10120                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10121                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10122                                                         "for the current encoding, insertion may be incorrect."),
10123                                                         shortfile);
10124                         } else if (res == COMPOSE_INSERT_SUCCESS)
10125                                 files_inserted++;
10126
10127                         g_free(shortfile);
10128                         g_free(filedup);
10129                         g_free(file);
10130                 }
10131                 g_list_free(file_list);
10132         }
10133
10134 #ifdef USE_ENCHANT      
10135         if (files_inserted > 0 && compose->gtkaspell && 
10136             compose->gtkaspell->check_while_typing)
10137                 gtkaspell_highlight_all(compose->gtkaspell);
10138 #endif
10139 }
10140
10141 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10142 {
10143         Compose *compose = (Compose *)data;
10144
10145         compose_insert_sig(compose, FALSE);
10146 }
10147
10148 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10149 {
10150         Compose *compose = (Compose *)data;
10151
10152         compose_insert_sig(compose, TRUE);
10153 }
10154
10155 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10156                               gpointer data)
10157 {
10158         gint x, y;
10159         Compose *compose = (Compose *)data;
10160
10161         gtkut_widget_get_uposition(widget, &x, &y);
10162         if (!compose->batch) {
10163                 prefs_common.compose_x = x;
10164                 prefs_common.compose_y = y;
10165         }
10166         if (compose->sending || compose->updating)
10167                 return TRUE;
10168         compose_close_cb(NULL, compose);
10169         return TRUE;
10170 }
10171
10172 void compose_close_toolbar(Compose *compose)
10173 {
10174         compose_close_cb(NULL, compose);
10175 }
10176
10177 static gboolean compose_can_autosave(Compose *compose)
10178 {
10179         if (compose->privacy_system && compose->use_encryption)
10180                 return prefs_common.autosave && prefs_common.autosave_encrypted;
10181         else
10182                 return prefs_common.autosave;
10183 }
10184
10185 static void compose_close_cb(GtkAction *action, gpointer data)
10186 {
10187         Compose *compose = (Compose *)data;
10188         AlertValue val;
10189
10190 #ifdef G_OS_UNIX
10191         if (compose->exteditor_tag != -1) {
10192                 if (!compose_ext_editor_kill(compose))
10193                         return;
10194         }
10195 #endif
10196
10197         if (compose->modified) {
10198                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10199                 if (!g_mutex_trylock(compose->mutex)) {
10200                         /* we don't want to lock the mutex once it's available,
10201                          * because as the only other part of compose.c locking
10202                          * it is compose_close - which means once unlocked,
10203                          * the compose struct will be freed */
10204                         debug_print("couldn't lock mutex, probably sending\n");
10205                         return;
10206                 }
10207                 if (!reedit) {
10208                         val = alertpanel(_("Discard message"),
10209                                  _("This message has been modified. Discard it?"),
10210                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10211                 } else {
10212                         val = alertpanel(_("Save changes"),
10213                                  _("This message has been modified. Save the latest changes?"),
10214                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10215                 }
10216                 g_mutex_unlock(compose->mutex);
10217                 switch (val) {
10218                 case G_ALERTDEFAULT:
10219                         if (compose_can_autosave(compose) && !reedit)
10220                                 compose_remove_draft(compose);
10221                         break;
10222                 case G_ALERTALTERNATE:
10223                         compose_draft(data, COMPOSE_QUIT_EDITING);
10224                         return;
10225                 default:
10226                         return;
10227                 }
10228         }
10229
10230         compose_close(compose);
10231 }
10232
10233 static void compose_print_cb(GtkAction *action, gpointer data)
10234 {
10235         Compose *compose = (Compose *) data;
10236
10237         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10238         if (compose->targetinfo)
10239                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10240 }
10241
10242 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10243 {
10244         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10245         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10246         Compose *compose = (Compose *) data;
10247
10248         if (active)
10249                 compose->out_encoding = (CharSet)value;
10250 }
10251
10252 static void compose_address_cb(GtkAction *action, gpointer data)
10253 {
10254         Compose *compose = (Compose *)data;
10255
10256 #ifndef USE_NEW_ADDRBOOK
10257         addressbook_open(compose);
10258 #else
10259         GError* error = NULL;
10260         addressbook_connect_signals(compose);
10261         addressbook_dbus_open(TRUE, &error);
10262         if (error) {
10263                 g_warning("%s", error->message);
10264                 g_error_free(error);
10265         }
10266 #endif
10267 }
10268
10269 static void about_show_cb(GtkAction *action, gpointer data)
10270 {
10271         about_show();
10272 }
10273
10274 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10275 {
10276         Compose *compose = (Compose *)data;
10277         Template *tmpl;
10278         gchar *msg;
10279         AlertValue val;
10280
10281         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10282         cm_return_if_fail(tmpl != NULL);
10283
10284         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10285                               tmpl->name);
10286         val = alertpanel(_("Apply template"), msg,
10287                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10288         g_free(msg);
10289
10290         if (val == G_ALERTDEFAULT)
10291                 compose_template_apply(compose, tmpl, TRUE);
10292         else if (val == G_ALERTALTERNATE)
10293                 compose_template_apply(compose, tmpl, FALSE);
10294 }
10295
10296 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10297 {
10298         Compose *compose = (Compose *)data;
10299
10300         compose_exec_ext_editor(compose);
10301 }
10302
10303 static void compose_undo_cb(GtkAction *action, gpointer data)
10304 {
10305         Compose *compose = (Compose *)data;
10306         gboolean prev_autowrap = compose->autowrap;
10307
10308         compose->autowrap = FALSE;
10309         undo_undo(compose->undostruct);
10310         compose->autowrap = prev_autowrap;
10311 }
10312
10313 static void compose_redo_cb(GtkAction *action, gpointer data)
10314 {
10315         Compose *compose = (Compose *)data;
10316         gboolean prev_autowrap = compose->autowrap;
10317         
10318         compose->autowrap = FALSE;
10319         undo_redo(compose->undostruct);
10320         compose->autowrap = prev_autowrap;
10321 }
10322
10323 static void entry_cut_clipboard(GtkWidget *entry)
10324 {
10325         if (GTK_IS_EDITABLE(entry))
10326                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10327         else if (GTK_IS_TEXT_VIEW(entry))
10328                 gtk_text_buffer_cut_clipboard(
10329                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10330                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10331                         TRUE);
10332 }
10333
10334 static void entry_copy_clipboard(GtkWidget *entry)
10335 {
10336         if (GTK_IS_EDITABLE(entry))
10337                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10338         else if (GTK_IS_TEXT_VIEW(entry))
10339                 gtk_text_buffer_copy_clipboard(
10340                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10341                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10342 }
10343
10344 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10345                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10346 {
10347         if (GTK_IS_TEXT_VIEW(entry)) {
10348                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10349                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10350                 GtkTextIter start_iter, end_iter;
10351                 gint start, end;
10352                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10353
10354                 if (contents == NULL)
10355                         return;
10356         
10357                 /* we shouldn't delete the selection when middle-click-pasting, or we
10358                  * can't mid-click-paste our own selection */
10359                 if (clip != GDK_SELECTION_PRIMARY) {
10360                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10361                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10362                 }
10363                 
10364                 if (insert_place == NULL) {
10365                         /* if insert_place isn't specified, insert at the cursor.
10366                          * used for Ctrl-V pasting */
10367                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10368                         start = gtk_text_iter_get_offset(&start_iter);
10369                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10370                 } else {
10371                         /* if insert_place is specified, paste here.
10372                          * used for mid-click-pasting */
10373                         start = gtk_text_iter_get_offset(insert_place);
10374                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10375                         if (prefs_common.primary_paste_unselects)
10376                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10377                 }
10378                 
10379                 if (!wrap) {
10380                         /* paste unwrapped: mark the paste so it's not wrapped later */
10381                         end = start + strlen(contents);
10382                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10383                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10384                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10385                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10386                         /* rewrap paragraph now (after a mid-click-paste) */
10387                         mark_start = gtk_text_buffer_get_insert(buffer);
10388                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10389                         gtk_text_iter_backward_char(&start_iter);
10390                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10391                 }
10392         } else if (GTK_IS_EDITABLE(entry))
10393                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10394
10395         compose->modified = TRUE;
10396 }
10397
10398 static void entry_allsel(GtkWidget *entry)
10399 {
10400         if (GTK_IS_EDITABLE(entry))
10401                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10402         else if (GTK_IS_TEXT_VIEW(entry)) {
10403                 GtkTextIter startiter, enditer;
10404                 GtkTextBuffer *textbuf;
10405
10406                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10407                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10408                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10409
10410                 gtk_text_buffer_move_mark_by_name(textbuf, 
10411                         "selection_bound", &startiter);
10412                 gtk_text_buffer_move_mark_by_name(textbuf, 
10413                         "insert", &enditer);
10414         }
10415 }
10416
10417 static void compose_cut_cb(GtkAction *action, gpointer data)
10418 {
10419         Compose *compose = (Compose *)data;
10420         if (compose->focused_editable 
10421 #ifndef GENERIC_UMPC
10422             && gtk_widget_has_focus(compose->focused_editable)
10423 #endif
10424             )
10425                 entry_cut_clipboard(compose->focused_editable);
10426 }
10427
10428 static void compose_copy_cb(GtkAction *action, gpointer data)
10429 {
10430         Compose *compose = (Compose *)data;
10431         if (compose->focused_editable 
10432 #ifndef GENERIC_UMPC
10433             && gtk_widget_has_focus(compose->focused_editable)
10434 #endif
10435             )
10436                 entry_copy_clipboard(compose->focused_editable);
10437 }
10438
10439 static void compose_paste_cb(GtkAction *action, gpointer data)
10440 {
10441         Compose *compose = (Compose *)data;
10442         gint prev_autowrap;
10443         GtkTextBuffer *buffer;
10444         BLOCK_WRAP();
10445         if (compose->focused_editable &&
10446 #ifndef GENERIC_UMPC
10447             gtk_widget_has_focus(compose->focused_editable)
10448 #endif
10449                 )
10450                 entry_paste_clipboard(compose, compose->focused_editable, 
10451                                 prefs_common.linewrap_pastes,
10452                                 GDK_SELECTION_CLIPBOARD, NULL);
10453         UNBLOCK_WRAP();
10454
10455 #ifdef USE_ENCHANT
10456         if (
10457 #ifndef GENERIC_UMPC
10458                 gtk_widget_has_focus(compose->text) &&
10459 #endif
10460             compose->gtkaspell && 
10461             compose->gtkaspell->check_while_typing)
10462                 gtkaspell_highlight_all(compose->gtkaspell);
10463 #endif
10464 }
10465
10466 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10467 {
10468         Compose *compose = (Compose *)data;
10469         gint wrap_quote = prefs_common.linewrap_quote;
10470         if (compose->focused_editable 
10471 #ifndef GENERIC_UMPC
10472             && gtk_widget_has_focus(compose->focused_editable)
10473 #endif
10474             ) {
10475                 /* let text_insert() (called directly or at a later time
10476                  * after the gtk_editable_paste_clipboard) know that 
10477                  * text is to be inserted as a quotation. implemented
10478                  * by using a simple refcount... */
10479                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10480                                                 G_OBJECT(compose->focused_editable),
10481                                                 "paste_as_quotation"));
10482                 g_object_set_data(G_OBJECT(compose->focused_editable),
10483                                     "paste_as_quotation",
10484                                     GINT_TO_POINTER(paste_as_quotation + 1));
10485                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10486                 entry_paste_clipboard(compose, compose->focused_editable, 
10487                                 prefs_common.linewrap_pastes,
10488                                 GDK_SELECTION_CLIPBOARD, NULL);
10489                 prefs_common.linewrap_quote = wrap_quote;
10490         }
10491 }
10492
10493 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10494 {
10495         Compose *compose = (Compose *)data;
10496         gint prev_autowrap;
10497         GtkTextBuffer *buffer;
10498         BLOCK_WRAP();
10499         if (compose->focused_editable 
10500 #ifndef GENERIC_UMPC
10501             && gtk_widget_has_focus(compose->focused_editable)
10502 #endif
10503             )
10504                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10505                         GDK_SELECTION_CLIPBOARD, NULL);
10506         UNBLOCK_WRAP();
10507
10508 #ifdef USE_ENCHANT
10509         if (
10510 #ifndef GENERIC_UMPC
10511                 gtk_widget_has_focus(compose->text) &&
10512 #endif
10513             compose->gtkaspell && 
10514             compose->gtkaspell->check_while_typing)
10515                 gtkaspell_highlight_all(compose->gtkaspell);
10516 #endif
10517 }
10518
10519 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10520 {
10521         Compose *compose = (Compose *)data;
10522         gint prev_autowrap;
10523         GtkTextBuffer *buffer;
10524         BLOCK_WRAP();
10525         if (compose->focused_editable 
10526 #ifndef GENERIC_UMPC
10527             && gtk_widget_has_focus(compose->focused_editable)
10528 #endif
10529             )
10530                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10531                         GDK_SELECTION_CLIPBOARD, NULL);
10532         UNBLOCK_WRAP();
10533
10534 #ifdef USE_ENCHANT
10535         if (
10536 #ifndef GENERIC_UMPC
10537                 gtk_widget_has_focus(compose->text) &&
10538 #endif
10539             compose->gtkaspell &&
10540             compose->gtkaspell->check_while_typing)
10541                 gtkaspell_highlight_all(compose->gtkaspell);
10542 #endif
10543 }
10544
10545 static void compose_allsel_cb(GtkAction *action, gpointer data)
10546 {
10547         Compose *compose = (Compose *)data;
10548         if (compose->focused_editable 
10549 #ifndef GENERIC_UMPC
10550             && gtk_widget_has_focus(compose->focused_editable)
10551 #endif
10552             )
10553                 entry_allsel(compose->focused_editable);
10554 }
10555
10556 static void textview_move_beginning_of_line (GtkTextView *text)
10557 {
10558         GtkTextBuffer *buffer;
10559         GtkTextMark *mark;
10560         GtkTextIter ins;
10561
10562         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10563
10564         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10565         mark = gtk_text_buffer_get_insert(buffer);
10566         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10567         gtk_text_iter_set_line_offset(&ins, 0);
10568         gtk_text_buffer_place_cursor(buffer, &ins);
10569 }
10570
10571 static void textview_move_forward_character (GtkTextView *text)
10572 {
10573         GtkTextBuffer *buffer;
10574         GtkTextMark *mark;
10575         GtkTextIter ins;
10576
10577         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10578
10579         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10580         mark = gtk_text_buffer_get_insert(buffer);
10581         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10582         if (gtk_text_iter_forward_cursor_position(&ins))
10583                 gtk_text_buffer_place_cursor(buffer, &ins);
10584 }
10585
10586 static void textview_move_backward_character (GtkTextView *text)
10587 {
10588         GtkTextBuffer *buffer;
10589         GtkTextMark *mark;
10590         GtkTextIter ins;
10591
10592         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10593
10594         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10595         mark = gtk_text_buffer_get_insert(buffer);
10596         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10597         if (gtk_text_iter_backward_cursor_position(&ins))
10598                 gtk_text_buffer_place_cursor(buffer, &ins);
10599 }
10600
10601 static void textview_move_forward_word (GtkTextView *text)
10602 {
10603         GtkTextBuffer *buffer;
10604         GtkTextMark *mark;
10605         GtkTextIter ins;
10606         gint count;
10607
10608         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10609
10610         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10611         mark = gtk_text_buffer_get_insert(buffer);
10612         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10613         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10614         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10615                 gtk_text_iter_backward_word_start(&ins);
10616                 gtk_text_buffer_place_cursor(buffer, &ins);
10617         }
10618 }
10619
10620 static void textview_move_backward_word (GtkTextView *text)
10621 {
10622         GtkTextBuffer *buffer;
10623         GtkTextMark *mark;
10624         GtkTextIter ins;
10625
10626         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10627
10628         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10629         mark = gtk_text_buffer_get_insert(buffer);
10630         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10631         if (gtk_text_iter_backward_word_starts(&ins, 1))
10632                 gtk_text_buffer_place_cursor(buffer, &ins);
10633 }
10634
10635 static void textview_move_end_of_line (GtkTextView *text)
10636 {
10637         GtkTextBuffer *buffer;
10638         GtkTextMark *mark;
10639         GtkTextIter ins;
10640
10641         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10642
10643         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10644         mark = gtk_text_buffer_get_insert(buffer);
10645         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10646         if (gtk_text_iter_forward_to_line_end(&ins))
10647                 gtk_text_buffer_place_cursor(buffer, &ins);
10648 }
10649
10650 static void textview_move_next_line (GtkTextView *text)
10651 {
10652         GtkTextBuffer *buffer;
10653         GtkTextMark *mark;
10654         GtkTextIter ins;
10655         gint offset;
10656
10657         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10658
10659         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10660         mark = gtk_text_buffer_get_insert(buffer);
10661         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10662         offset = gtk_text_iter_get_line_offset(&ins);
10663         if (gtk_text_iter_forward_line(&ins)) {
10664                 gtk_text_iter_set_line_offset(&ins, offset);
10665                 gtk_text_buffer_place_cursor(buffer, &ins);
10666         }
10667 }
10668
10669 static void textview_move_previous_line (GtkTextView *text)
10670 {
10671         GtkTextBuffer *buffer;
10672         GtkTextMark *mark;
10673         GtkTextIter ins;
10674         gint offset;
10675
10676         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10677
10678         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10679         mark = gtk_text_buffer_get_insert(buffer);
10680         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10681         offset = gtk_text_iter_get_line_offset(&ins);
10682         if (gtk_text_iter_backward_line(&ins)) {
10683                 gtk_text_iter_set_line_offset(&ins, offset);
10684                 gtk_text_buffer_place_cursor(buffer, &ins);
10685         }
10686 }
10687
10688 static void textview_delete_forward_character (GtkTextView *text)
10689 {
10690         GtkTextBuffer *buffer;
10691         GtkTextMark *mark;
10692         GtkTextIter ins, end_iter;
10693
10694         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10695
10696         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10697         mark = gtk_text_buffer_get_insert(buffer);
10698         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10699         end_iter = ins;
10700         if (gtk_text_iter_forward_char(&end_iter)) {
10701                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10702         }
10703 }
10704
10705 static void textview_delete_backward_character (GtkTextView *text)
10706 {
10707         GtkTextBuffer *buffer;
10708         GtkTextMark *mark;
10709         GtkTextIter ins, end_iter;
10710
10711         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10712
10713         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10714         mark = gtk_text_buffer_get_insert(buffer);
10715         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10716         end_iter = ins;
10717         if (gtk_text_iter_backward_char(&end_iter)) {
10718                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10719         }
10720 }
10721
10722 static void textview_delete_forward_word (GtkTextView *text)
10723 {
10724         GtkTextBuffer *buffer;
10725         GtkTextMark *mark;
10726         GtkTextIter ins, end_iter;
10727
10728         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10729
10730         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10731         mark = gtk_text_buffer_get_insert(buffer);
10732         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10733         end_iter = ins;
10734         if (gtk_text_iter_forward_word_end(&end_iter)) {
10735                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10736         }
10737 }
10738
10739 static void textview_delete_backward_word (GtkTextView *text)
10740 {
10741         GtkTextBuffer *buffer;
10742         GtkTextMark *mark;
10743         GtkTextIter ins, end_iter;
10744
10745         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10746
10747         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10748         mark = gtk_text_buffer_get_insert(buffer);
10749         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10750         end_iter = ins;
10751         if (gtk_text_iter_backward_word_start(&end_iter)) {
10752                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10753         }
10754 }
10755
10756 static void textview_delete_line (GtkTextView *text)
10757 {
10758         GtkTextBuffer *buffer;
10759         GtkTextMark *mark;
10760         GtkTextIter ins, start_iter, end_iter;
10761
10762         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10763
10764         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10765         mark = gtk_text_buffer_get_insert(buffer);
10766         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10767
10768         start_iter = ins;
10769         gtk_text_iter_set_line_offset(&start_iter, 0);
10770
10771         end_iter = ins;
10772         if (gtk_text_iter_ends_line(&end_iter)){
10773                 if (!gtk_text_iter_forward_char(&end_iter))
10774                         gtk_text_iter_backward_char(&start_iter);
10775         }
10776         else 
10777                 gtk_text_iter_forward_to_line_end(&end_iter);
10778         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10779 }
10780
10781 static void textview_delete_to_line_end (GtkTextView *text)
10782 {
10783         GtkTextBuffer *buffer;
10784         GtkTextMark *mark;
10785         GtkTextIter ins, end_iter;
10786
10787         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10788
10789         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10790         mark = gtk_text_buffer_get_insert(buffer);
10791         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10792         end_iter = ins;
10793         if (gtk_text_iter_ends_line(&end_iter))
10794                 gtk_text_iter_forward_char(&end_iter);
10795         else
10796                 gtk_text_iter_forward_to_line_end(&end_iter);
10797         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10798 }
10799
10800 #define DO_ACTION(name, act) {                                          \
10801         if(!strcmp(name, a_name)) {                                     \
10802                 return act;                                             \
10803         }                                                               \
10804 }
10805 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10806 {
10807         const gchar *a_name = gtk_action_get_name(action);
10808         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10809         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10810         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10811         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10812         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10813         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10814         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10815         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10816         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10817         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10818         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10819         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10820         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10821         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10822         return -1;
10823 }
10824
10825 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10826 {
10827         Compose *compose = (Compose *)data;
10828         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10829         ComposeCallAdvancedAction action = -1;
10830         
10831         action = compose_call_advanced_action_from_path(gaction);
10832
10833         static struct {
10834                 void (*do_action) (GtkTextView *text);
10835         } action_table[] = {
10836                 {textview_move_beginning_of_line},
10837                 {textview_move_forward_character},
10838                 {textview_move_backward_character},
10839                 {textview_move_forward_word},
10840                 {textview_move_backward_word},
10841                 {textview_move_end_of_line},
10842                 {textview_move_next_line},
10843                 {textview_move_previous_line},
10844                 {textview_delete_forward_character},
10845                 {textview_delete_backward_character},
10846                 {textview_delete_forward_word},
10847                 {textview_delete_backward_word},
10848                 {textview_delete_line},
10849                 {textview_delete_to_line_end}
10850         };
10851
10852         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10853
10854         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10855             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10856                 if (action_table[action].do_action)
10857                         action_table[action].do_action(text);
10858                 else
10859                         g_warning("Not implemented yet.");
10860         }
10861 }
10862
10863 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10864 {
10865         GtkAllocation allocation;
10866         GtkWidget *parent;
10867         gchar *str = NULL;
10868         
10869         if (GTK_IS_EDITABLE(widget)) {
10870                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10871                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10872                         strlen(str));
10873                 g_free(str);
10874                 if ((parent = gtk_widget_get_parent(widget))
10875                  && (parent = gtk_widget_get_parent(parent))
10876                  && (parent = gtk_widget_get_parent(parent))) {
10877                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
10878                                 gtk_widget_get_allocation(widget, &allocation);
10879                                 gint y = allocation.y;
10880                                 gint height = allocation.height;
10881                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10882                                         (GTK_SCROLLED_WINDOW(parent));
10883
10884                                 gfloat value = gtk_adjustment_get_value(shown);
10885                                 gfloat upper = gtk_adjustment_get_upper(shown);
10886                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
10887                                 if (y < (int)value) {
10888                                         gtk_adjustment_set_value(shown, y - 1);
10889                                 }
10890                                 if ((y + height) > ((int)value + (int)page_size)) {
10891                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
10892                                                 gtk_adjustment_set_value(shown, 
10893                                                         y + height - (int)page_size - 1);
10894                                         } else {
10895                                                 gtk_adjustment_set_value(shown, 
10896                                                         (int)upper - (int)page_size - 1);
10897                                         }
10898                                 }
10899                         }
10900                 }
10901         }
10902
10903         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10904                 compose->focused_editable = widget;
10905         
10906 #ifdef GENERIC_UMPC
10907         if (GTK_IS_TEXT_VIEW(widget) 
10908             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10909                 g_object_ref(compose->notebook);
10910                 g_object_ref(compose->edit_vbox);
10911                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10912                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10913                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10914                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10915                 g_object_unref(compose->notebook);
10916                 g_object_unref(compose->edit_vbox);
10917                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10918                                         G_CALLBACK(compose_grab_focus_cb),
10919                                         compose);
10920                 gtk_widget_grab_focus(widget);
10921                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10922                                         G_CALLBACK(compose_grab_focus_cb),
10923                                         compose);
10924         } else if (!GTK_IS_TEXT_VIEW(widget) 
10925                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10926                 g_object_ref(compose->notebook);
10927                 g_object_ref(compose->edit_vbox);
10928                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10929                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10930                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10931                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10932                 g_object_unref(compose->notebook);
10933                 g_object_unref(compose->edit_vbox);
10934                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10935                                         G_CALLBACK(compose_grab_focus_cb),
10936                                         compose);
10937                 gtk_widget_grab_focus(widget);
10938                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10939                                         G_CALLBACK(compose_grab_focus_cb),
10940                                         compose);
10941         }
10942 #endif
10943 }
10944
10945 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10946 {
10947         compose->modified = TRUE;
10948 //      compose_beautify_paragraph(compose, NULL, TRUE);
10949 #ifndef GENERIC_UMPC
10950         compose_set_title(compose);
10951 #endif
10952 }
10953
10954 static void compose_wrap_cb(GtkAction *action, gpointer data)
10955 {
10956         Compose *compose = (Compose *)data;
10957         compose_beautify_paragraph(compose, NULL, TRUE);
10958 }
10959
10960 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10961 {
10962         Compose *compose = (Compose *)data;
10963         compose_wrap_all_full(compose, TRUE);
10964 }
10965
10966 static void compose_find_cb(GtkAction *action, gpointer data)
10967 {
10968         Compose *compose = (Compose *)data;
10969
10970         message_search_compose(compose);
10971 }
10972
10973 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10974                                          gpointer        data)
10975 {
10976         Compose *compose = (Compose *)data;
10977         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10978         if (compose->autowrap)
10979                 compose_wrap_all_full(compose, TRUE);
10980         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10981 }
10982
10983 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10984                                          gpointer        data)
10985 {
10986         Compose *compose = (Compose *)data;
10987         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10988 }
10989
10990 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10991 {
10992         Compose *compose = (Compose *)data;
10993
10994         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10995 }
10996
10997 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10998 {
10999         Compose *compose = (Compose *)data;
11000
11001         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11002 }
11003
11004 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
11005 {
11006         g_free(compose->privacy_system);
11007
11008         compose->privacy_system = g_strdup(account->default_privacy_system);
11009         compose_update_privacy_system_menu_item(compose, warn);
11010 }
11011
11012 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
11013 {
11014         Compose *compose = (Compose *)data;
11015
11016         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
11017                 gtk_widget_show(compose->ruler_hbox);
11018                 prefs_common.show_ruler = TRUE;
11019         } else {
11020                 gtk_widget_hide(compose->ruler_hbox);
11021                 gtk_widget_queue_resize(compose->edit_vbox);
11022                 prefs_common.show_ruler = FALSE;
11023         }
11024 }
11025
11026 static void compose_attach_drag_received_cb (GtkWidget          *widget,
11027                                              GdkDragContext     *context,
11028                                              gint                x,
11029                                              gint                y,
11030                                              GtkSelectionData   *data,
11031                                              guint               info,
11032                                              guint               time,
11033                                              gpointer            user_data)
11034 {
11035         Compose *compose = (Compose *)user_data;
11036         GList *list, *tmp;
11037         GdkAtom type;
11038
11039         type = gtk_selection_data_get_data_type(data);
11040         if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11041 #ifdef G_OS_WIN32
11042          || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
11043 #endif
11044            ) && gtk_drag_get_source_widget(context) != 
11045                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11046                 list = uri_list_extract_filenames(
11047                         (const gchar *)gtk_selection_data_get_data(data));
11048                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11049                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11050                         compose_attach_append
11051                                 (compose, (const gchar *)tmp->data,
11052                                  utf8_filename, NULL, NULL);
11053                         g_free(utf8_filename);
11054                 }
11055                 if (list) compose_changed_cb(NULL, compose);
11056                 list_free_strings(list);
11057                 g_list_free(list);
11058         } else if (gtk_drag_get_source_widget(context) 
11059                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11060                 /* comes from our summaryview */
11061                 SummaryView * summaryview = NULL;
11062                 GSList * list = NULL, *cur = NULL;
11063                 
11064                 if (mainwindow_get_mainwindow())
11065                         summaryview = mainwindow_get_mainwindow()->summaryview;
11066                 
11067                 if (summaryview)
11068                         list = summary_get_selected_msg_list(summaryview);
11069                 
11070                 for (cur = list; cur; cur = cur->next) {
11071                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11072                         gchar *file = NULL;
11073                         if (msginfo)
11074                                 file = procmsg_get_message_file_full(msginfo, 
11075                                         TRUE, TRUE);
11076                         if (file) {
11077                                 compose_attach_append(compose, (const gchar *)file, 
11078                                         (const gchar *)file, "message/rfc822", NULL);
11079                                 g_free(file);
11080                         }
11081                 }
11082                 g_slist_free(list);
11083         }
11084 }
11085
11086 static gboolean compose_drag_drop(GtkWidget *widget,
11087                                   GdkDragContext *drag_context,
11088                                   gint x, gint y,
11089                                   guint time, gpointer user_data)
11090 {
11091         /* not handling this signal makes compose_insert_drag_received_cb
11092          * called twice */
11093         return TRUE;                                     
11094 }
11095
11096 static gboolean completion_set_focus_to_subject
11097                                         (GtkWidget    *widget,
11098                                          GdkEventKey  *event,
11099                                          Compose      *compose)
11100 {
11101         cm_return_val_if_fail(compose != NULL, FALSE);
11102
11103         /* make backtab move to subject field */
11104         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11105                 gtk_widget_grab_focus(compose->subject_entry);
11106                 return TRUE;
11107         }
11108         return FALSE;
11109 }
11110
11111 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11112                                              GdkDragContext     *drag_context,
11113                                              gint                x,
11114                                              gint                y,
11115                                              GtkSelectionData   *data,
11116                                              guint               info,
11117                                              guint               time,
11118                                              gpointer            user_data)
11119 {
11120         Compose *compose = (Compose *)user_data;
11121         GList *list, *tmp;
11122         GdkAtom type;
11123
11124         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11125          * does not work */
11126         type = gtk_selection_data_get_data_type(data);
11127 #ifndef G_OS_WIN32
11128         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11129 #else
11130         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
11131 #endif
11132                 AlertValue val = G_ALERTDEFAULT;
11133                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11134
11135                 list = uri_list_extract_filenames(ddata);
11136                 if (list == NULL && strstr(ddata, "://")) {
11137                         /* Assume a list of no files, and data has ://, is a remote link */
11138                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11139                         gchar *tmpfile = get_tmp_file();
11140                         str_write_to_file(tmpdata, tmpfile);
11141                         g_free(tmpdata);  
11142                         compose_insert_file(compose, tmpfile);
11143                         claws_unlink(tmpfile);
11144                         g_free(tmpfile);
11145                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11146                         compose_beautify_paragraph(compose, NULL, TRUE);
11147                         return;
11148                 }
11149                 switch (prefs_common.compose_dnd_mode) {
11150                         case COMPOSE_DND_ASK:
11151                                 val = alertpanel_full(_("Insert or attach?"),
11152                                          _("Do you want to insert the contents of the file(s) "
11153                                            "into the message body, or attach it to the email?"),
11154                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
11155                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11156                                 break;
11157                         case COMPOSE_DND_INSERT:
11158                                 val = G_ALERTALTERNATE;
11159                                 break;
11160                         case COMPOSE_DND_ATTACH:
11161                                 val = G_ALERTOTHER;
11162                                 break;
11163                         default:
11164                                 /* unexpected case */
11165                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11166                 }
11167
11168                 if (val & G_ALERTDISABLE) {
11169                         val &= ~G_ALERTDISABLE;
11170                         /* remember what action to perform by default, only if we don't click Cancel */
11171                         if (val == G_ALERTALTERNATE)
11172                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11173                         else if (val == G_ALERTOTHER)
11174                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11175                 }
11176
11177                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11178                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11179                         list_free_strings(list);
11180                         g_list_free(list);
11181                         return;
11182                 } else if (val == G_ALERTOTHER) {
11183                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11184                         list_free_strings(list);
11185                         g_list_free(list);
11186                         return;
11187                 } 
11188
11189                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11190                         compose_insert_file(compose, (const gchar *)tmp->data);
11191                 }
11192                 list_free_strings(list);
11193                 g_list_free(list);
11194                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11195                 return;
11196         }
11197 }
11198
11199 static void compose_header_drag_received_cb (GtkWidget          *widget,
11200                                              GdkDragContext     *drag_context,
11201                                              gint                x,
11202                                              gint                y,
11203                                              GtkSelectionData   *data,
11204                                              guint               info,
11205                                              guint               time,
11206                                              gpointer            user_data)
11207 {
11208         GtkEditable *entry = (GtkEditable *)user_data;
11209         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11210
11211         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11212          * does not work */
11213
11214         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11215                 gchar *decoded=g_new(gchar, strlen(email));
11216                 int start = 0;
11217
11218                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11219                 gtk_editable_delete_text(entry, 0, -1);
11220                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11221                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11222                 g_free(decoded);
11223                 return;
11224         }
11225         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11226 }
11227
11228 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11229 {
11230         Compose *compose = (Compose *)data;
11231
11232         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11233                 compose->return_receipt = TRUE;
11234         else
11235                 compose->return_receipt = FALSE;
11236 }
11237
11238 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11239 {
11240         Compose *compose = (Compose *)data;
11241
11242         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11243                 compose->remove_references = TRUE;
11244         else
11245                 compose->remove_references = FALSE;
11246 }
11247
11248 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11249                                         ComposeHeaderEntry *headerentry)
11250 {
11251         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11252         return FALSE;
11253 }
11254
11255 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11256                                             GdkEventKey *event,
11257                                             ComposeHeaderEntry *headerentry)
11258 {
11259         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11260             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11261             !(event->state & GDK_MODIFIER_MASK) &&
11262             (event->keyval == GDK_KEY_BackSpace) &&
11263             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11264                 gtk_container_remove
11265                         (GTK_CONTAINER(headerentry->compose->header_table),
11266                          headerentry->combo);
11267                 gtk_container_remove
11268                         (GTK_CONTAINER(headerentry->compose->header_table),
11269                          headerentry->entry);
11270                 headerentry->compose->header_list =
11271                         g_slist_remove(headerentry->compose->header_list,
11272                                        headerentry);
11273                 g_free(headerentry);
11274         } else  if (event->keyval == GDK_KEY_Tab) {
11275                 if (headerentry->compose->header_last == headerentry) {
11276                         /* Override default next focus, and give it to subject_entry
11277                          * instead of notebook tabs
11278                          */
11279                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11280                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11281                         return TRUE;
11282                 }
11283         }
11284         return FALSE;
11285 }
11286
11287 static gboolean scroll_postpone(gpointer data)
11288 {
11289         Compose *compose = (Compose *)data;
11290
11291         if (compose->batch)
11292                 return FALSE;
11293
11294         GTK_EVENTS_FLUSH();
11295         compose_show_first_last_header(compose, FALSE);
11296         return FALSE;
11297 }
11298
11299 static void compose_headerentry_changed_cb(GtkWidget *entry,
11300                                     ComposeHeaderEntry *headerentry)
11301 {
11302         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11303                 compose_create_header_entry(headerentry->compose);
11304                 g_signal_handlers_disconnect_matched
11305                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11306                          0, 0, NULL, NULL, headerentry);
11307
11308                 if (!headerentry->compose->batch)
11309                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11310         }
11311 }
11312
11313 static gboolean compose_defer_auto_save_draft(Compose *compose)
11314 {
11315         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11316         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11317         return FALSE;
11318 }
11319
11320 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11321 {
11322         GtkAdjustment *vadj;
11323
11324         cm_return_if_fail(compose);
11325
11326         if(compose->batch)
11327                 return;
11328
11329         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11330         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11331         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11332                                 gtk_widget_get_parent(compose->header_table)));
11333         gtk_adjustment_set_value(vadj, (show_first ?
11334                                 gtk_adjustment_get_lower(vadj) :
11335                                 (gtk_adjustment_get_upper(vadj) -
11336                                 gtk_adjustment_get_page_size(vadj))));
11337         gtk_adjustment_changed(vadj);
11338 }
11339
11340 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11341                           const gchar *text, gint len, Compose *compose)
11342 {
11343         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11344                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11345         GtkTextMark *mark;
11346
11347         cm_return_if_fail(text != NULL);
11348
11349         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11350                                         G_CALLBACK(text_inserted),
11351                                         compose);
11352         if (paste_as_quotation) {
11353                 gchar *new_text;
11354                 const gchar *qmark;
11355                 guint pos = 0;
11356                 GtkTextIter start_iter;
11357
11358                 if (len < 0)
11359                         len = strlen(text);
11360
11361                 new_text = g_strndup(text, len);
11362
11363                 qmark = compose_quote_char_from_context(compose);
11364
11365                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11366                 gtk_text_buffer_place_cursor(buffer, iter);
11367
11368                 pos = gtk_text_iter_get_offset(iter);
11369
11370                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11371                                                   _("Quote format error at line %d."));
11372                 quote_fmt_reset_vartable();
11373                 g_free(new_text);
11374                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11375                                   GINT_TO_POINTER(paste_as_quotation - 1));
11376                                   
11377                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11378                 gtk_text_buffer_place_cursor(buffer, iter);
11379                 gtk_text_buffer_delete_mark(buffer, mark);
11380
11381                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11382                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11383                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11384                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11385                 gtk_text_buffer_delete_mark(buffer, mark);
11386         } else {
11387                 if (strcmp(text, "\n") || compose->automatic_break
11388                 || gtk_text_iter_starts_line(iter)) {
11389                         GtkTextIter before_ins;
11390                         gtk_text_buffer_insert(buffer, iter, text, len);
11391                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11392                                 before_ins = *iter; 
11393                                 gtk_text_iter_backward_chars(&before_ins, len);
11394                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11395                         }
11396                 } else {
11397                         /* check if the preceding is just whitespace or quote */
11398                         GtkTextIter start_line;
11399                         gchar *tmp = NULL, *quote = NULL;
11400                         gint quote_len = 0, is_normal = 0;
11401                         start_line = *iter;
11402                         gtk_text_iter_set_line_offset(&start_line, 0); 
11403                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11404                         g_strstrip(tmp);
11405
11406                         if (*tmp == '\0') {
11407                                 is_normal = 1;
11408                         } else {
11409                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11410                                 if (quote)
11411                                         is_normal = 1;
11412                                 g_free(quote);
11413                         }
11414                         g_free(tmp);
11415                         
11416                         if (is_normal) {
11417                                 gtk_text_buffer_insert(buffer, iter, text, len);
11418                         } else {
11419                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11420                                         iter, text, len, "no_join", NULL);
11421                         }
11422                 }
11423         }
11424         
11425         if (!paste_as_quotation) {
11426                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11427                 compose_beautify_paragraph(compose, iter, FALSE);
11428                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11429                 gtk_text_buffer_delete_mark(buffer, mark);
11430         }
11431
11432         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11433                                           G_CALLBACK(text_inserted),
11434                                           compose);
11435         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11436
11437         if (compose_can_autosave(compose) && 
11438             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11439             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11440                 compose->draft_timeout_tag = g_timeout_add
11441                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11442 }
11443
11444 #if USE_ENCHANT
11445 static void compose_check_all(GtkAction *action, gpointer data)
11446 {
11447         Compose *compose = (Compose *)data;
11448         if (!compose->gtkaspell)
11449                 return;
11450                 
11451         if (gtk_widget_has_focus(compose->subject_entry))
11452                 claws_spell_entry_check_all(
11453                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11454         else
11455                 gtkaspell_check_all(compose->gtkaspell);
11456 }
11457
11458 static void compose_highlight_all(GtkAction *action, gpointer data)
11459 {
11460         Compose *compose = (Compose *)data;
11461         if (compose->gtkaspell) {
11462                 claws_spell_entry_recheck_all(
11463                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11464                 gtkaspell_highlight_all(compose->gtkaspell);
11465         }
11466 }
11467
11468 static void compose_check_backwards(GtkAction *action, gpointer data)
11469 {
11470         Compose *compose = (Compose *)data;
11471         if (!compose->gtkaspell) {
11472                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11473                 return;
11474         }
11475
11476         if (gtk_widget_has_focus(compose->subject_entry))
11477                 claws_spell_entry_check_backwards(
11478                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11479         else
11480                 gtkaspell_check_backwards(compose->gtkaspell);
11481 }
11482
11483 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11484 {
11485         Compose *compose = (Compose *)data;
11486         if (!compose->gtkaspell) {
11487                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11488                 return;
11489         }
11490
11491         if (gtk_widget_has_focus(compose->subject_entry))
11492                 claws_spell_entry_check_forwards_go(
11493                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11494         else
11495                 gtkaspell_check_forwards_go(compose->gtkaspell);
11496 }
11497 #endif
11498
11499 /*!
11500  *\brief        Guess originating forward account from MsgInfo and several 
11501  *              "common preference" settings. Return NULL if no guess. 
11502  */
11503 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11504 {
11505         PrefsAccount *account = NULL;
11506         
11507         cm_return_val_if_fail(msginfo, NULL);
11508         cm_return_val_if_fail(msginfo->folder, NULL);
11509         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11510
11511         if (msginfo->folder->prefs->enable_default_account)
11512                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11513                 
11514         if (!account) 
11515                 account = msginfo->folder->folder->account;
11516                 
11517         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11518                 gchar *to;
11519                 Xstrdup_a(to, msginfo->to, return NULL);
11520                 extract_address(to);
11521                 account = account_find_from_address(to, FALSE);
11522         }
11523
11524         if (!account && prefs_common.forward_account_autosel) {
11525                 gchar cc[BUFFSIZE];
11526                 if (!procheader_get_header_from_msginfo
11527                         (msginfo, cc,sizeof cc , "Cc:")) { 
11528                         gchar *buf = cc + strlen("Cc:");
11529                         extract_address(buf);
11530                         account = account_find_from_address(buf, FALSE);
11531                 }
11532         }
11533         
11534         if (!account && prefs_common.forward_account_autosel) {
11535                 gchar deliveredto[BUFFSIZE];
11536                 if (!procheader_get_header_from_msginfo
11537                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11538                         gchar *buf = deliveredto + strlen("Delivered-To:");
11539                         extract_address(buf);
11540                         account = account_find_from_address(buf, FALSE);
11541                 }
11542         }
11543         
11544         return account;
11545 }
11546
11547 gboolean compose_close(Compose *compose)
11548 {
11549         gint x, y;
11550
11551         cm_return_val_if_fail(compose, FALSE);
11552
11553         if (!g_mutex_trylock(compose->mutex)) {
11554                 /* we have to wait for the (possibly deferred by auto-save)
11555                  * drafting to be done, before destroying the compose under
11556                  * it. */
11557                 debug_print("waiting for drafting to finish...\n");
11558                 compose_allow_user_actions(compose, FALSE);
11559                 if (compose->close_timeout_tag == 0) {
11560                         compose->close_timeout_tag = 
11561                                 g_timeout_add (500, (GSourceFunc) compose_close,
11562                                 compose);
11563                 }
11564                 return TRUE;
11565         }
11566
11567         if (compose->draft_timeout_tag >= 0) {
11568                 g_source_remove(compose->draft_timeout_tag);
11569                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11570         }
11571
11572         gtkut_widget_get_uposition(compose->window, &x, &y);
11573         if (!compose->batch) {
11574                 prefs_common.compose_x = x;
11575                 prefs_common.compose_y = y;
11576         }
11577         g_mutex_unlock(compose->mutex);
11578         compose_destroy(compose);
11579         return FALSE;
11580 }
11581
11582 /**
11583  * Add entry field for each address in list.
11584  * \param compose     E-Mail composition object.
11585  * \param listAddress List of (formatted) E-Mail addresses.
11586  */
11587 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11588         GList *node;
11589         gchar *addr;
11590         node = listAddress;
11591         while( node ) {
11592                 addr = ( gchar * ) node->data;
11593                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11594                 node = g_list_next( node );
11595         }
11596 }
11597
11598 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11599                                     guint action, gboolean opening_multiple)
11600 {
11601         gchar *body = NULL;
11602         GSList *new_msglist = NULL;
11603         MsgInfo *tmp_msginfo = NULL;
11604         gboolean originally_enc = FALSE;
11605         gboolean originally_sig = FALSE;
11606         Compose *compose = NULL;
11607         gchar *s_system = NULL;
11608
11609         cm_return_if_fail(msgview != NULL);
11610
11611         cm_return_if_fail(msginfo_list != NULL);
11612
11613         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11614                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11615                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11616
11617                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11618                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11619                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11620                                                 orig_msginfo, mimeinfo);
11621                         if (tmp_msginfo != NULL) {
11622                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11623
11624                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11625                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11626                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11627
11628                                 tmp_msginfo->folder = orig_msginfo->folder;
11629                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11630                                 if (orig_msginfo->tags) {
11631                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11632                                         tmp_msginfo->folder->tags_dirty = TRUE;
11633                                 }
11634                         }
11635                 }
11636         }
11637
11638         if (!opening_multiple)
11639                 body = messageview_get_selection(msgview);
11640
11641         if (new_msglist) {
11642                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11643                 procmsg_msginfo_free(tmp_msginfo);
11644                 g_slist_free(new_msglist);
11645         } else
11646                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11647
11648         if (compose && originally_enc) {
11649                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11650         }
11651
11652         if (compose && originally_sig && compose->account->default_sign_reply) {
11653                 compose_force_signing(compose, compose->account, s_system);
11654         }
11655         g_free(s_system);
11656         g_free(body);
11657         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11658 }
11659
11660 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11661                                     guint action)
11662 {
11663         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11664         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11665                 GSList *cur = msginfo_list;
11666                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11667                                                "messages. Opening the windows "
11668                                                "could take some time. Do you "
11669                                                "want to continue?"), 
11670                                                g_slist_length(msginfo_list));
11671                 if (g_slist_length(msginfo_list) > 9
11672                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11673                     != G_ALERTALTERNATE) {
11674                         g_free(msg);
11675                         return;
11676                 }
11677                 g_free(msg);
11678                 /* We'll open multiple compose windows */
11679                 /* let the WM place the next windows */
11680                 compose_force_window_origin = FALSE;
11681                 for (; cur; cur = cur->next) {
11682                         GSList tmplist;
11683                         tmplist.data = cur->data;
11684                         tmplist.next = NULL;
11685                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11686                 }
11687                 compose_force_window_origin = TRUE;
11688         } else {
11689                 /* forwarding multiple mails as attachments is done via a
11690                  * single compose window */
11691                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11692         }
11693 }
11694
11695 void compose_check_for_email_account(Compose *compose)
11696 {
11697         PrefsAccount *ac = NULL, *curr = NULL;
11698         GList *list;
11699         
11700         if (!compose)
11701                 return;
11702
11703         if (compose->account && compose->account->protocol == A_NNTP) {
11704                 ac = account_get_cur_account();
11705                 if (ac->protocol == A_NNTP) {
11706                         list = account_get_list();
11707                         
11708                         for( ; list != NULL ; list = g_list_next(list)) {
11709                                 curr = (PrefsAccount *) list->data;
11710                                 if (curr->protocol != A_NNTP) {
11711                                         ac = curr;
11712                                         break;
11713                                 }
11714                         }
11715                 }
11716                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11717                                         ac->account_id); 
11718         }
11719 }
11720
11721 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11722                                 const gchar *address)
11723 {
11724         GSList *msginfo_list = NULL;
11725         gchar *body =  messageview_get_selection(msgview);
11726         Compose *compose;
11727         
11728         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11729         
11730         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11731         compose_check_for_email_account(compose);
11732         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11733         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11734         compose_reply_set_subject(compose, msginfo);
11735
11736         g_free(body);
11737         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11738 }
11739
11740 void compose_set_position(Compose *compose, gint pos)
11741 {
11742         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11743
11744         gtkut_text_view_set_position(text, pos);
11745 }
11746
11747 gboolean compose_search_string(Compose *compose,
11748                                 const gchar *str, gboolean case_sens)
11749 {
11750         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11751
11752         return gtkut_text_view_search_string(text, str, case_sens);
11753 }
11754
11755 gboolean compose_search_string_backward(Compose *compose,
11756                                 const gchar *str, gboolean case_sens)
11757 {
11758         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11759
11760         return gtkut_text_view_search_string_backward(text, str, case_sens);
11761 }
11762
11763 /* allocate a msginfo structure and populate its data from a compose data structure */
11764 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11765 {
11766         MsgInfo *newmsginfo;
11767         GSList *list;
11768         gchar buf[BUFFSIZE];
11769
11770         cm_return_val_if_fail( compose != NULL, NULL );
11771
11772         newmsginfo = procmsg_msginfo_new();
11773
11774         /* date is now */
11775         get_rfc822_date(buf, sizeof(buf));
11776         newmsginfo->date = g_strdup(buf);
11777
11778         /* from */
11779         if (compose->from_name) {
11780                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11781                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11782         }
11783
11784         /* subject */
11785         if (compose->subject_entry)
11786                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11787
11788         /* to, cc, reply-to, newsgroups */
11789         for (list = compose->header_list; list; list = list->next) {
11790                 gchar *header = gtk_editable_get_chars(
11791                                                                 GTK_EDITABLE(
11792                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11793                 gchar *entry = gtk_editable_get_chars(
11794                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11795
11796                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11797                         if ( newmsginfo->to == NULL ) {
11798                                 newmsginfo->to = g_strdup(entry);
11799                         } else if (entry && *entry) {
11800                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11801                                 g_free(newmsginfo->to);
11802                                 newmsginfo->to = tmp;
11803                         }
11804                 } else
11805                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11806                         if ( newmsginfo->cc == NULL ) {
11807                                 newmsginfo->cc = g_strdup(entry);
11808                         } else if (entry && *entry) {
11809                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11810                                 g_free(newmsginfo->cc);
11811                                 newmsginfo->cc = tmp;
11812                         }
11813                 } else
11814                 if ( strcasecmp(header,
11815                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11816                         if ( newmsginfo->newsgroups == NULL ) {
11817                                 newmsginfo->newsgroups = g_strdup(entry);
11818                         } else if (entry && *entry) {
11819                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11820                                 g_free(newmsginfo->newsgroups);
11821                                 newmsginfo->newsgroups = tmp;
11822                         }
11823                 }
11824
11825                 g_free(header);
11826                 g_free(entry);  
11827         }
11828
11829         /* other data is unset */
11830
11831         return newmsginfo;
11832 }
11833
11834 #ifdef USE_ENCHANT
11835 /* update compose's dictionaries from folder dict settings */
11836 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11837                                                 FolderItem *folder_item)
11838 {
11839         cm_return_if_fail(compose != NULL);
11840
11841         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11842                 FolderItemPrefs *prefs = folder_item->prefs;
11843
11844                 if (prefs->enable_default_dictionary)
11845                         gtkaspell_change_dict(compose->gtkaspell,
11846                                         prefs->default_dictionary, FALSE);
11847                 if (folder_item->prefs->enable_default_alt_dictionary)
11848                         gtkaspell_change_alt_dict(compose->gtkaspell,
11849                                         prefs->default_alt_dictionary);
11850                 if (prefs->enable_default_dictionary
11851                         || prefs->enable_default_alt_dictionary)
11852                         compose_spell_menu_changed(compose);
11853         }
11854 }
11855 #endif
11856
11857 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11858 {
11859         Compose *compose = (Compose *)data;
11860
11861         cm_return_if_fail(compose != NULL);
11862
11863         gtk_widget_grab_focus(compose->text);
11864 }
11865
11866 static void from_name_activate_cb(GtkWidget *widget, gpointer data)
11867 {
11868         gtk_combo_box_popup(GTK_COMBO_BOX(data));
11869 }
11870
11871
11872 /*
11873  * End of Source.
11874  */