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