Fix two missed return values in compose_write_to_file() from commit e9f610a4f.
[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"),
5222                                         privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5223                         break;
5224                 case COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED:
5225                         alertpanel_error(_("Could not queue message for sending:\n\n"
5226                                                 "Encryption failed: %s"),
5227                                         privacy_peek_error() ? privacy_get_error() : _("Unknown error"));
5228                         break;
5229                 case COMPOSE_QUEUE_ERROR_CHAR_CONVERSION:
5230                         alertpanel_error(_("Could not queue message for sending:\n\n"
5231                                                 "Charset conversion failed."));
5232                         break;
5233                 case COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY:
5234                         alertpanel_error(_("Could not queue message for sending:\n\n"
5235                                                 "Couldn't get recipient encryption key."));
5236                         break;
5237                 default:
5238                         /* unhandled error */
5239                         debug_print("oops, unhandled compose_queue() return value %d\n",
5240                                         val);
5241                         break;
5242         }
5243 }
5244
5245 gint compose_send(Compose *compose)
5246 {
5247         gint msgnum;
5248         FolderItem *folder = NULL;
5249         ComposeQueueResult val = COMPOSE_QUEUE_ERROR_NO_MSG;
5250         gchar *msgpath = NULL;
5251         gboolean discard_window = FALSE;
5252         gchar *errstr = NULL;
5253         gchar *tmsgid = NULL;
5254         MainWindow *mainwin = mainwindow_get_mainwindow();
5255         gboolean queued_removed = FALSE;
5256
5257         if (prefs_common.send_dialog_invisible
5258                         || compose->batch == TRUE)
5259                 discard_window = TRUE;
5260
5261         compose_allow_user_actions (compose, FALSE);
5262         compose->sending = TRUE;
5263
5264         if (compose_check_entries(compose, TRUE) == FALSE) {
5265                 if (compose->batch) {
5266                         gtk_widget_show_all(compose->window);
5267                 }
5268                 goto bail;
5269         }
5270
5271         inc_lock();
5272         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5273
5274         if (val != COMPOSE_QUEUE_SUCCESS) {
5275                 if (compose->batch) {
5276                         gtk_widget_show_all(compose->window);
5277                 }
5278
5279                 _display_queue_error(val);
5280
5281                 goto bail;
5282         }
5283
5284         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5285         if (discard_window) {
5286                 compose->sending = FALSE;
5287                 compose_close(compose);
5288                 /* No more compose access in the normal codepath 
5289                  * after this point! */
5290                 compose = NULL;
5291         }
5292
5293         if (msgnum == 0) {
5294                 alertpanel_error(_("The message was queued but could not be "
5295                                    "sent.\nUse \"Send queued messages\" from "
5296                                    "the main window to retry."));
5297                 if (!discard_window) {
5298                         goto bail;
5299                 }
5300                 inc_unlock();
5301                 g_free(tmsgid);
5302                 return -1;
5303         }
5304         if (msgpath == NULL) {
5305                 msgpath = folder_item_fetch_msg(folder, msgnum);
5306                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5307                 g_free(msgpath);
5308         } else {
5309                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5310                 claws_unlink(msgpath);
5311                 g_free(msgpath);
5312         }
5313         if (!discard_window) {
5314                 if (val != 0) {
5315                         if (!queued_removed)
5316                                 folder_item_remove_msg(folder, msgnum);
5317                         folder_item_scan(folder);
5318                         if (tmsgid) {
5319                                 /* make sure we delete that */
5320                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5321                                 if (tmp) {
5322                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5323                                         folder_item_remove_msg(folder, tmp->msgnum);
5324                                         procmsg_msginfo_free(&tmp);
5325                                 } 
5326                         }
5327                 }
5328         }
5329
5330         if (val == 0) {
5331                 if (!queued_removed)
5332                         folder_item_remove_msg(folder, msgnum);
5333                 folder_item_scan(folder);
5334                 if (tmsgid) {
5335                         /* make sure we delete that */
5336                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5337                         if (tmp) {
5338                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5339                                 folder_item_remove_msg(folder, tmp->msgnum);
5340                                 procmsg_msginfo_free(&tmp);
5341                         }
5342                 }
5343                 if (!discard_window) {
5344                         compose->sending = FALSE;
5345                         compose_allow_user_actions (compose, TRUE);
5346                         compose_close(compose);
5347                 }
5348         } else {
5349                 if (errstr) {
5350                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5351                                    "the main window to retry."), errstr);
5352                         g_free(errstr);
5353                 } else {
5354                         alertpanel_error_log(_("The message was queued but could not be "
5355                                    "sent.\nUse \"Send queued messages\" from "
5356                                    "the main window to retry."));
5357                 }
5358                 if (!discard_window) {
5359                         goto bail;              
5360                 }
5361                 inc_unlock();
5362                 g_free(tmsgid);
5363                 return -1;
5364         }
5365         g_free(tmsgid);
5366         inc_unlock();
5367         toolbar_main_set_sensitive(mainwin);
5368         main_window_set_menu_sensitive(mainwin);
5369         return 0;
5370
5371 bail:
5372         inc_unlock();
5373         g_free(tmsgid);
5374         compose_allow_user_actions (compose, TRUE);
5375         compose->sending = FALSE;
5376         compose->modified = TRUE; 
5377         toolbar_main_set_sensitive(mainwin);
5378         main_window_set_menu_sensitive(mainwin);
5379
5380         return -1;
5381 }
5382
5383 static gboolean compose_use_attach(Compose *compose) 
5384 {
5385         GtkTreeModel *model = gtk_tree_view_get_model
5386                                 (GTK_TREE_VIEW(compose->attach_clist));
5387         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5388 }
5389
5390 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5391                                                            FILE *fp)
5392 {
5393         gchar buf[BUFFSIZE];
5394         gchar *str;
5395         gboolean first_to_address;
5396         gboolean first_cc_address;
5397         GSList *list;
5398         ComposeHeaderEntry *headerentry;
5399         const gchar *headerentryname;
5400         const gchar *cc_hdr;
5401         const gchar *to_hdr;
5402         gboolean err = FALSE;
5403
5404         debug_print("Writing redirect header\n");
5405
5406         cc_hdr = prefs_common_translated_header_name("Cc:");
5407         to_hdr = prefs_common_translated_header_name("To:");
5408
5409         first_to_address = TRUE;
5410         for (list = compose->header_list; list; list = list->next) {
5411                 headerentry = ((ComposeHeaderEntry *)list->data);
5412                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5413
5414                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5415                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5416                         Xstrdup_a(str, entstr, return -1);
5417                         g_strstrip(str);
5418                         if (str[0] != '\0') {
5419                                 compose_convert_header
5420                                         (compose, buf, sizeof(buf), str,
5421                                         strlen("Resent-To") + 2, TRUE);
5422
5423                                 if (first_to_address) {
5424                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5425                                         first_to_address = FALSE;
5426                                 } else {
5427                                         err |= (fprintf(fp, ",") < 0);
5428                                 }
5429                                 err |= (fprintf(fp, "%s", buf) < 0);
5430                         }
5431                 }
5432         }
5433         if (!first_to_address) {
5434                 err |= (fprintf(fp, "\n") < 0);
5435         }
5436
5437         first_cc_address = TRUE;
5438         for (list = compose->header_list; list; list = list->next) {
5439                 headerentry = ((ComposeHeaderEntry *)list->data);
5440                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5441
5442                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5443                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5444                         Xstrdup_a(str, strg, return -1);
5445                         g_strstrip(str);
5446                         if (str[0] != '\0') {
5447                                 compose_convert_header
5448                                         (compose, buf, sizeof(buf), str,
5449                                         strlen("Resent-Cc") + 2, TRUE);
5450
5451                                 if (first_cc_address) {
5452                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5453                                         first_cc_address = FALSE;
5454                                 } else {
5455                                         err |= (fprintf(fp, ",") < 0);
5456                                 }
5457                                 err |= (fprintf(fp, "%s", buf) < 0);
5458                         }
5459                 }
5460         }
5461         if (!first_cc_address) {
5462                 err |= (fprintf(fp, "\n") < 0);
5463         }
5464         
5465         return (err ? -1:0);
5466 }
5467
5468 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5469 {
5470         gchar date[RFC822_DATE_BUFFSIZE];
5471         gchar buf[BUFFSIZE];
5472         gchar *str;
5473         const gchar *entstr;
5474         /* struct utsname utsbuf; */
5475         gboolean err = FALSE;
5476
5477         cm_return_val_if_fail(fp != NULL, -1);
5478         cm_return_val_if_fail(compose->account != NULL, -1);
5479         cm_return_val_if_fail(compose->account->address != NULL, -1);
5480
5481         /* Resent-Date */
5482         if (prefs_common.hide_timezone)
5483                 get_rfc822_date_hide_tz(date, sizeof(date));
5484         else
5485                 get_rfc822_date(date, sizeof(date));
5486         err |= (fprintf(fp, "Resent-Date: %s\n", date) < 0);
5487
5488         /* Resent-From */
5489         if (compose->account->name && *compose->account->name) {
5490                 compose_convert_header
5491                         (compose, buf, sizeof(buf), compose->account->name,
5492                          strlen("From: "), TRUE);
5493                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5494                         buf, compose->account->address) < 0);
5495         } else
5496                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5497
5498         /* Subject */
5499         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5500         if (*entstr != '\0') {
5501                 Xstrdup_a(str, entstr, return -1);
5502                 g_strstrip(str);
5503                 if (*str != '\0') {
5504                         compose_convert_header(compose, buf, sizeof(buf), str,
5505                                                strlen("Subject: "), FALSE);
5506                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5507                 }
5508         }
5509
5510         /* Resent-Message-ID */
5511         if (compose->account->gen_msgid) {
5512                 gchar *addr = prefs_account_generate_msgid(compose->account);
5513                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", addr) < 0);
5514                 if (compose->msgid)
5515                         g_free(compose->msgid);
5516                 compose->msgid = addr;
5517         } else {
5518                 compose->msgid = NULL;
5519         }
5520
5521         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5522                 return -1;
5523
5524         /* separator between header and body */
5525         err |= (fputs("\n", fp) == EOF);
5526
5527         return (err ? -1:0);
5528 }
5529
5530 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5531 {
5532         FILE *fp;
5533         size_t len;
5534         gchar *buf = NULL;
5535         gchar rewrite_buf[BUFFSIZE];
5536         int i = 0;
5537         gboolean skip = FALSE;
5538         gboolean err = FALSE;
5539         gchar *not_included[]={
5540                 "Return-Path:",         "Delivered-To:",        "Received:",
5541                 "Subject:",             "X-UIDL:",              "AF:",
5542                 "NF:",                  "PS:",                  "SRH:",
5543                 "SFN:",                 "DSR:",                 "MID:",
5544                 "CFG:",                 "PT:",                  "S:",
5545                 "RQ:",                  "SSV:",                 "NSV:",
5546                 "SSH:",                 "R:",                   "MAID:",
5547                 "NAID:",                "RMID:",                "FMID:",
5548                 "SCF:",                 "RRCPT:",               "NG:",
5549                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5550                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5551                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5552                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5553                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5554                 NULL
5555                 };
5556         gint ret = 0;
5557
5558         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5559                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5560                 return -1;
5561         }
5562
5563         while ((ret = procheader_get_one_field_asis(&buf, fp)) != -1) {
5564                 skip = FALSE;
5565                 for (i = 0; not_included[i] != NULL; i++) {
5566                         if (g_ascii_strncasecmp(buf, not_included[i],
5567                                                 strlen(not_included[i])) == 0) {
5568                                 skip = TRUE;
5569                                 break;
5570                         }
5571                 }
5572                 if (skip) {
5573                         g_free(buf);
5574                         buf = NULL;
5575                         continue;
5576                 }
5577                 if (fputs(buf, fdest) == -1) {
5578                         g_free(buf);
5579                         buf = NULL;
5580                         goto error;
5581                 }
5582
5583                 if (!prefs_common.redirect_keep_from) {
5584                         if (g_ascii_strncasecmp(buf, "From:",
5585                                           strlen("From:")) == 0) {
5586                                 err |= (fputs(" (by way of ", fdest) == EOF);
5587                                 if (compose->account->name
5588                                     && *compose->account->name) {
5589                                         gchar buffer[BUFFSIZE];
5590
5591                                         compose_convert_header
5592                                                 (compose, buffer, sizeof(buffer),
5593                                                  compose->account->name,
5594                                                  strlen("From: "),
5595                                                  FALSE);
5596                                         err |= (fprintf(fdest, "%s <%s>",
5597                                                 buffer,
5598                                                 compose->account->address) < 0);
5599                                 } else
5600                                         err |= (fprintf(fdest, "%s",
5601                                                 compose->account->address) < 0);
5602                                 err |= (fputs(")", fdest) == EOF);
5603                         }
5604                 }
5605
5606                 g_free(buf);
5607                 buf = NULL;
5608                 if (fputs("\n", fdest) == -1)
5609                         goto error;
5610         }
5611
5612         if (err)
5613                 goto error;
5614
5615         if (compose_redirect_write_headers(compose, fdest))
5616                 goto error;
5617
5618         while ((len = fread(rewrite_buf, sizeof(gchar), sizeof(rewrite_buf), fp)) > 0) {
5619                 if (fwrite(rewrite_buf, sizeof(gchar), len, fdest) != len)
5620                         goto error;
5621         }
5622
5623         fclose(fp);
5624
5625         return 0;
5626
5627 error:
5628         fclose(fp);
5629
5630         return -1;
5631 }
5632
5633 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5634 {
5635         GtkTextBuffer *buffer;
5636         GtkTextIter start, end, tmp;
5637         gchar *chars, *tmp_enc_file, *content;
5638         gchar *buf, *msg;
5639         const gchar *out_codeset;
5640         EncodingType encoding = ENC_UNKNOWN;
5641         MimeInfo *mimemsg, *mimetext;
5642         gint line;
5643         const gchar *src_codeset = CS_INTERNAL;
5644         gchar *from_addr = NULL;
5645         gchar *from_name = NULL;
5646         FolderItem *outbox;
5647
5648         if (action == COMPOSE_WRITE_FOR_SEND) {
5649                 attach_parts = TRUE;
5650
5651                 /* We're sending the message, generate a Message-ID
5652                  * if necessary. */
5653                 if (compose->msgid == NULL &&
5654                                 compose->account->gen_msgid) {
5655                         compose->msgid = prefs_account_generate_msgid(compose->account);
5656                 }
5657         }
5658
5659         /* create message MimeInfo */
5660         mimemsg = procmime_mimeinfo_new();
5661         mimemsg->type = MIMETYPE_MESSAGE;
5662         mimemsg->subtype = g_strdup("rfc822");
5663         mimemsg->content = MIMECONTENT_MEM;
5664         mimemsg->tmp = TRUE; /* must free content later */
5665         mimemsg->data.mem = compose_get_header(compose);
5666
5667         /* Create text part MimeInfo */
5668         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5669         gtk_text_buffer_get_end_iter(buffer, &end);
5670         tmp = end;
5671
5672         /* We make sure that there is a newline at the end. */
5673         if (action == COMPOSE_WRITE_FOR_SEND && gtk_text_iter_backward_char(&tmp)) {
5674                 chars = gtk_text_buffer_get_text(buffer, &tmp, &end, FALSE);
5675                 if (*chars != '\n') {
5676                         gtk_text_buffer_insert(buffer, &end, "\n", 1);
5677                 }
5678                 g_free(chars);
5679         }
5680
5681         /* get all composed text */
5682         gtk_text_buffer_get_start_iter(buffer, &start);
5683         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5684
5685         out_codeset = conv_get_charset_str(compose->out_encoding);
5686
5687         if (!out_codeset && is_ascii_str(chars)) {
5688                 out_codeset = CS_US_ASCII;
5689         } else if (prefs_common.outgoing_fallback_to_ascii &&
5690                    is_ascii_str(chars)) {
5691                 out_codeset = CS_US_ASCII;
5692                 encoding = ENC_7BIT;
5693         }
5694
5695         if (!out_codeset) {
5696                 gchar *test_conv_global_out = NULL;
5697                 gchar *test_conv_reply = NULL;
5698
5699                 /* automatic mode. be automatic. */
5700                 codeconv_set_strict(TRUE);
5701
5702                 out_codeset = conv_get_outgoing_charset_str();
5703                 if (out_codeset) {
5704                         debug_print("trying to convert to %s\n", out_codeset);
5705                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5706                 }
5707
5708                 if (!test_conv_global_out && compose->orig_charset
5709                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5710                         out_codeset = compose->orig_charset;
5711                         debug_print("failure; trying to convert to %s\n", out_codeset);
5712                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5713                 }
5714
5715                 if (!test_conv_global_out && !test_conv_reply) {
5716                         /* we're lost */
5717                         out_codeset = CS_INTERNAL;
5718                         debug_print("failure; finally using %s\n", out_codeset);
5719                 }
5720                 g_free(test_conv_global_out);
5721                 g_free(test_conv_reply);
5722                 codeconv_set_strict(FALSE);
5723         }
5724
5725         if (encoding == ENC_UNKNOWN) {
5726                 if (prefs_common.encoding_method == CTE_BASE64)
5727                         encoding = ENC_BASE64;
5728                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5729                         encoding = ENC_QUOTED_PRINTABLE;
5730                 else if (prefs_common.encoding_method == CTE_8BIT)
5731                         encoding = ENC_8BIT;
5732                 else
5733                         encoding = procmime_get_encoding_for_charset(out_codeset);
5734         }
5735
5736         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5737                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5738
5739         if (action == COMPOSE_WRITE_FOR_SEND) {
5740                 codeconv_set_strict(TRUE);
5741                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5742                 codeconv_set_strict(FALSE);
5743
5744                 if (!buf) {
5745                         AlertValue aval;
5746
5747                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5748                                                 "to the specified %s charset.\n"
5749                                                 "Send it as %s?"), out_codeset, src_codeset);
5750                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL,
5751                                                _("_Send"), NULL, ALERTFOCUS_SECOND, FALSE,
5752                                               NULL, ALERT_ERROR);
5753                         g_free(msg);
5754
5755                         if (aval != G_ALERTALTERNATE) {
5756                                 g_free(chars);
5757                                 return COMPOSE_QUEUE_ERROR_CHAR_CONVERSION;
5758                         } else {
5759                                 buf = chars;
5760                                 out_codeset = src_codeset;
5761                                 chars = NULL;
5762                         }
5763                 }
5764         } else {
5765                 buf = chars;
5766                 out_codeset = src_codeset;
5767                 chars = NULL;
5768         }
5769         g_free(chars);
5770
5771         if (prefs_common.rewrite_first_from && (encoding == ENC_8BIT || encoding == ENC_7BIT)) {
5772                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5773                     strstr(buf, "\nFrom ") != NULL) {
5774                         encoding = ENC_QUOTED_PRINTABLE;
5775                 }
5776         }
5777
5778         mimetext = procmime_mimeinfo_new();
5779         mimetext->content = MIMECONTENT_MEM;
5780         mimetext->tmp = TRUE; /* must free content later */
5781         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5782          * and free the data, which we need later. */
5783         mimetext->data.mem = g_strdup(buf); 
5784         mimetext->type = MIMETYPE_TEXT;
5785         mimetext->subtype = g_strdup("plain");
5786         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5787                             g_strdup(out_codeset));
5788                             
5789         /* protect trailing spaces when signing message */
5790         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5791             privacy_system_can_sign(compose->privacy_system)) {
5792                 encoding = ENC_QUOTED_PRINTABLE;
5793         }
5794
5795 #ifdef G_OS_WIN32
5796         debug_print("main text: %Id bytes encoded as %s in %d\n",
5797 #else
5798         debug_print("main text: %zd bytes encoded as %s in %d\n",
5799 #endif
5800                 strlen(buf), out_codeset, encoding);
5801
5802         /* check for line length limit */
5803         if (action == COMPOSE_WRITE_FOR_SEND &&
5804             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5805             check_line_length(buf, 1000, &line) < 0) {
5806                 AlertValue aval;
5807
5808                 msg = g_strdup_printf
5809                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5810                            "The contents of the message might be broken on the way to the delivery.\n"
5811                            "\n"
5812                            "Send it anyway?"), line + 1);
5813                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL,
5814                                 ALERTFOCUS_FIRST);
5815                 g_free(msg);
5816                 if (aval != G_ALERTALTERNATE) {
5817                         g_free(buf);
5818                         return COMPOSE_QUEUE_ERROR_NO_MSG;
5819                 }
5820         }
5821         
5822         if (encoding != ENC_UNKNOWN)
5823                 procmime_encode_content(mimetext, encoding);
5824
5825         /* append attachment parts */
5826         if (compose_use_attach(compose) && attach_parts) {
5827                 MimeInfo *mimempart;
5828                 gchar *boundary = NULL;
5829                 mimempart = procmime_mimeinfo_new();
5830                 mimempart->content = MIMECONTENT_EMPTY;
5831                 mimempart->type = MIMETYPE_MULTIPART;
5832                 mimempart->subtype = g_strdup("mixed");
5833
5834                 do {
5835                         g_free(boundary);
5836                         boundary = generate_mime_boundary(NULL);
5837                 } while (strstr(buf, boundary) != NULL);
5838
5839                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5840                                     boundary);
5841
5842                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5843
5844                 g_node_append(mimempart->node, mimetext->node);
5845                 g_node_append(mimemsg->node, mimempart->node);
5846
5847                 if (compose_add_attachments(compose, mimempart) < 0)
5848                         return COMPOSE_QUEUE_ERROR_NO_MSG;
5849         } else
5850                 g_node_append(mimemsg->node, mimetext->node);
5851
5852         g_free(buf);
5853
5854         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5855                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5856                 /* extract name and address */
5857                 if (strstr(spec, " <") && strstr(spec, ">")) {
5858                         from_addr = g_strdup(strrchr(spec, '<')+1);
5859                         *(strrchr(from_addr, '>')) = '\0';
5860                         from_name = g_strdup(spec);
5861                         *(strrchr(from_name, '<')) = '\0';
5862                 } else {
5863                         from_name = NULL;
5864                         from_addr = NULL;
5865                 }
5866                 g_free(spec);
5867         }
5868         /* sign message if sending */
5869         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5870             privacy_system_can_sign(compose->privacy_system))
5871                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5872                         compose->account, from_addr)) {
5873                         g_free(from_name);
5874                         g_free(from_addr);
5875                         return COMPOSE_QUEUE_ERROR_SIGNING_FAILED;
5876         }
5877         g_free(from_name);
5878         g_free(from_addr);
5879
5880         if (compose->use_encryption) {
5881                 if (compose->encdata != NULL &&
5882                                 strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
5883
5884                         /* First, write an unencrypted copy and save it to outbox, if
5885                          * user wants that. */
5886                         if (compose->account->save_encrypted_as_clear_text) {
5887                                 debug_print("saving sent message unencrypted...\n");
5888                                 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
5889                                 if (tmpfp) {
5890                                         fclose(tmpfp);
5891
5892                                         /* fp now points to a file with headers written,
5893                                          * let's make a copy. */
5894                                         rewind(fp);
5895                                         content = file_read_stream_to_str(fp);
5896
5897                                         str_write_to_file(content, tmp_enc_file);
5898                                         g_free(content);
5899
5900                                         /* Now write the unencrypted body. */
5901                                         if ((tmpfp = g_fopen(tmp_enc_file, "a")) != NULL) {
5902                                                 procmime_write_mimeinfo(mimemsg, tmpfp);
5903                                                 fclose(tmpfp);
5904
5905                                                 outbox = folder_find_item_from_identifier(compose_get_save_to(compose));
5906                                                 if (!outbox)
5907                                                         outbox = folder_get_default_outbox();
5908
5909                                                 procmsg_save_to_outbox(outbox, tmp_enc_file, TRUE);
5910                                                 claws_unlink(tmp_enc_file);
5911                                         } else {
5912                                                 g_warning("Can't open file '%s'", tmp_enc_file);
5913                                         }
5914                                 } else {
5915                                         g_warning("couldn't get tempfile");
5916                                 }
5917                         }
5918                         if (!privacy_encrypt(compose->privacy_system, mimemsg, compose->encdata)) {
5919                                 debug_print("Couldn't encrypt mime structure: %s.\n",
5920                                                 privacy_get_error());
5921                                 return COMPOSE_QUEUE_ERROR_ENCRYPT_FAILED;
5922                         }
5923                 }
5924         }
5925
5926         procmime_write_mimeinfo(mimemsg, fp);
5927         
5928         procmime_mimeinfo_free_all(&mimemsg);
5929
5930         return 0;
5931 }
5932
5933 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5934 {
5935         GtkTextBuffer *buffer;
5936         GtkTextIter start, end;
5937         FILE *fp;
5938         size_t len;
5939         gchar *chars, *tmp;
5940
5941         if ((fp = g_fopen(file, "wb")) == NULL) {
5942                 FILE_OP_ERROR(file, "fopen");
5943                 return -1;
5944         }
5945
5946         /* chmod for security */
5947         if (change_file_mode_rw(fp, file) < 0) {
5948                 FILE_OP_ERROR(file, "chmod");
5949                 g_warning("can't change file mode");
5950         }
5951
5952         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5953         gtk_text_buffer_get_start_iter(buffer, &start);
5954         gtk_text_buffer_get_end_iter(buffer, &end);
5955         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5956
5957         chars = conv_codeset_strdup
5958                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5959
5960         g_free(tmp);
5961         if (!chars) {
5962                 fclose(fp);
5963                 claws_unlink(file);
5964                 return -1;
5965         }
5966         /* write body */
5967         len = strlen(chars);
5968         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5969                 FILE_OP_ERROR(file, "fwrite");
5970                 g_free(chars);
5971                 fclose(fp);
5972                 claws_unlink(file);
5973                 return -1;
5974         }
5975
5976         g_free(chars);
5977
5978         if (fclose(fp) == EOF) {
5979                 FILE_OP_ERROR(file, "fclose");
5980                 claws_unlink(file);
5981                 return -1;
5982         }
5983         return 0;
5984 }
5985
5986 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5987 {
5988         FolderItem *item;
5989         MsgInfo *msginfo = compose->targetinfo;
5990
5991         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5992         if (!msginfo) return -1;
5993
5994         if (!force && MSG_IS_LOCKED(msginfo->flags))
5995                 return 0;
5996
5997         item = msginfo->folder;
5998         cm_return_val_if_fail(item != NULL, -1);
5999
6000         if (procmsg_msg_exist(msginfo) &&
6001             (folder_has_parent_of_type(item, F_QUEUE) ||
6002              folder_has_parent_of_type(item, F_DRAFT) 
6003              || msginfo == compose->autosaved_draft)) {
6004                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
6005                         g_warning("can't remove the old message");
6006                         return -1;
6007                 } else {
6008                         debug_print("removed reedit target %d\n", msginfo->msgnum);
6009                 }
6010         }
6011
6012         return 0;
6013 }
6014
6015 static void compose_remove_draft(Compose *compose)
6016 {
6017         FolderItem *drafts;
6018         MsgInfo *msginfo = compose->targetinfo;
6019         drafts = account_get_special_folder(compose->account, F_DRAFT);
6020
6021         if (procmsg_msg_exist(msginfo)) {
6022                 folder_item_remove_msg(drafts, msginfo->msgnum);
6023         }
6024
6025 }
6026
6027 ComposeQueueResult compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
6028                    gboolean remove_reedit_target)
6029 {
6030         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
6031 }
6032
6033 static gboolean compose_warn_encryption(Compose *compose)
6034 {
6035         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
6036         AlertValue val = G_ALERTALTERNATE;
6037         
6038         if (warning == NULL)
6039                 return TRUE;
6040
6041         val = alertpanel_full(_("Encryption warning"), warning,
6042                   GTK_STOCK_CANCEL, _("C_ontinue"), NULL, ALERTFOCUS_SECOND,
6043                   TRUE, NULL, ALERT_WARNING);
6044         if (val & G_ALERTDISABLE) {
6045                 val &= ~G_ALERTDISABLE;
6046                 if (val == G_ALERTALTERNATE)
6047                         privacy_inhibit_encrypt_warning(compose->privacy_system,
6048                                 TRUE);
6049         }
6050
6051         if (val == G_ALERTALTERNATE) {
6052                 return TRUE;
6053         } else {
6054                 return FALSE;
6055         } 
6056 }
6057
6058 static ComposeQueueResult compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
6059                               gchar **msgpath, gboolean perform_checks,
6060                               gboolean remove_reedit_target)
6061 {
6062         FolderItem *queue;
6063         gchar *tmp;
6064         FILE *fp;
6065         GSList *cur;
6066         gint num;
6067         PrefsAccount *mailac = NULL, *newsac = NULL;
6068         gboolean err = FALSE;
6069
6070         debug_print("queueing message...\n");
6071         cm_return_val_if_fail(compose->account != NULL, -1);
6072
6073         if (compose_check_entries(compose, perform_checks) == FALSE) {
6074                 if (compose->batch) {
6075                         gtk_widget_show_all(compose->window);
6076                 }
6077                 return COMPOSE_QUEUE_ERROR_NO_MSG;
6078         }
6079
6080         if (!compose->to_list && !compose->newsgroup_list) {
6081                 g_warning("can't get recipient list.");
6082                 return COMPOSE_QUEUE_ERROR_NO_MSG;
6083         }
6084
6085         if (compose->to_list) {
6086                 if (compose->account->protocol != A_NNTP)
6087                         mailac = compose->account;
6088                 else if (cur_account && cur_account->protocol != A_NNTP)
6089                         mailac = cur_account;
6090                 else if (!(mailac = compose_current_mail_account())) {
6091                         alertpanel_error(_("No account for sending mails available!"));
6092                         return COMPOSE_QUEUE_ERROR_NO_MSG;
6093                 }
6094         }
6095
6096         if (compose->newsgroup_list) {
6097                 if (compose->account->protocol == A_NNTP)
6098                         newsac = compose->account;
6099                 else {
6100                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6101                         return COMPOSE_QUEUE_ERROR_NO_MSG;
6102                 }                       
6103         }
6104
6105         /* write queue header */
6106         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6107                               G_DIR_SEPARATOR, compose, (guint) rand());
6108         debug_print("queuing to %s\n", tmp);
6109         if ((fp = g_fopen(tmp, "w+b")) == NULL) {
6110                 FILE_OP_ERROR(tmp, "fopen");
6111                 g_free(tmp);
6112                 return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
6113         }
6114
6115         if (change_file_mode_rw(fp, tmp) < 0) {
6116                 FILE_OP_ERROR(tmp, "chmod");
6117                 g_warning("can't change file mode");
6118         }
6119
6120         /* queueing variables */
6121         err |= (fprintf(fp, "AF:\n") < 0);
6122         err |= (fprintf(fp, "NF:0\n") < 0);
6123         err |= (fprintf(fp, "PS:10\n") < 0);
6124         err |= (fprintf(fp, "SRH:1\n") < 0);
6125         err |= (fprintf(fp, "SFN:\n") < 0);
6126         err |= (fprintf(fp, "DSR:\n") < 0);
6127         if (compose->msgid)
6128                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
6129         else
6130                 err |= (fprintf(fp, "MID:\n") < 0);
6131         err |= (fprintf(fp, "CFG:\n") < 0);
6132         err |= (fprintf(fp, "PT:0\n") < 0);
6133         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
6134         err |= (fprintf(fp, "RQ:\n") < 0);
6135         if (mailac)
6136                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
6137         else
6138                 err |= (fprintf(fp, "SSV:\n") < 0);
6139         if (newsac)
6140                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
6141         else
6142                 err |= (fprintf(fp, "NSV:\n") < 0);
6143         err |= (fprintf(fp, "SSH:\n") < 0);
6144         /* write recepient list */
6145         if (compose->to_list) {
6146                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
6147                 for (cur = compose->to_list->next; cur != NULL;
6148                      cur = cur->next)
6149                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
6150                 err |= (fprintf(fp, "\n") < 0);
6151         }
6152         /* write newsgroup list */
6153         if (compose->newsgroup_list) {
6154                 err |= (fprintf(fp, "NG:") < 0);
6155                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
6156                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
6157                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
6158                 err |= (fprintf(fp, "\n") < 0);
6159         }
6160         /* Sylpheed account IDs */
6161         if (mailac)
6162                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
6163         if (newsac)
6164                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
6165
6166         
6167         if (compose->privacy_system != NULL) {
6168                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
6169                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
6170                 if (compose->use_encryption) {
6171                         if (!compose_warn_encryption(compose)) {
6172                                 fclose(fp);
6173                                 claws_unlink(tmp);
6174                                 g_free(tmp);
6175                                 return COMPOSE_QUEUE_ERROR_NO_MSG;
6176                         }
6177                         if (mailac && mailac->encrypt_to_self) {
6178                                 GSList *tmp_list = g_slist_copy(compose->to_list);
6179                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
6180                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
6181                                 g_slist_free(tmp_list);
6182                         } else {
6183                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
6184                         }
6185                         if (compose->encdata != NULL) {
6186                                 if (strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
6187                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6188                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
6189                                                 compose->encdata) < 0);
6190                                 } /* else we finally dont want to encrypt */
6191                         } else {
6192                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6193                                 /* and if encdata was null, it means there's been a problem in 
6194                                  * key selection */
6195                                 if (err == TRUE)
6196                                         g_warning("failed to write queue message");
6197                                 fclose(fp);
6198                                 claws_unlink(tmp);
6199                                 g_free(tmp);
6200                                 return COMPOSE_QUEUE_ERROR_NO_ENCRYPTION_KEY;
6201                         }
6202                 }
6203         }
6204
6205         /* Save copy folder */
6206         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6207                 gchar *savefolderid;
6208                 
6209                 savefolderid = compose_get_save_to(compose);
6210                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
6211                 g_free(savefolderid);
6212         }
6213         /* Save copy folder */
6214         if (compose->return_receipt) {
6215                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
6216         }
6217         /* Message-ID of message replying to */
6218         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
6219                 gchar *folderid = NULL;
6220
6221                 if (compose->replyinfo->folder)
6222                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
6223                 if (folderid == NULL)
6224                         folderid = g_strdup("NULL");
6225
6226                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
6227                 g_free(folderid);
6228         }
6229         /* Message-ID of message forwarding to */
6230         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
6231                 gchar *folderid = NULL;
6232                 
6233                 if (compose->fwdinfo->folder)
6234                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
6235                 if (folderid == NULL)
6236                         folderid = g_strdup("NULL");
6237
6238                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
6239                 g_free(folderid);
6240         }
6241
6242         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
6243         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
6244
6245         /* end of headers */
6246         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
6247
6248         if (compose->redirect_filename != NULL) {
6249                 if (compose_redirect_write_to_file(compose, fp) < 0) {
6250                         fclose(fp);
6251                         claws_unlink(tmp);
6252                         g_free(tmp);
6253                         return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
6254                 }
6255         } else {
6256                 gint result = 0;
6257                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
6258                         fclose(fp);
6259                         claws_unlink(tmp);
6260                         g_free(tmp);
6261                         return result;
6262                 }
6263         }
6264         if (err == TRUE) {
6265                 g_warning("failed to write queue message");
6266                 fclose(fp);
6267                 claws_unlink(tmp);
6268                 g_free(tmp);
6269                 return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
6270         }
6271         if (fclose(fp) == EOF) {
6272                 FILE_OP_ERROR(tmp, "fclose");
6273                 claws_unlink(tmp);
6274                 g_free(tmp);
6275                 return COMPOSE_QUEUE_ERROR_WITH_ERRNO;
6276         }
6277
6278         if (item && *item) {
6279                 queue = *item;
6280         } else {
6281                 queue = account_get_special_folder(compose->account, F_QUEUE);
6282         }
6283         if (!queue) {
6284                 g_warning("can't find queue folder");
6285                 claws_unlink(tmp);
6286                 g_free(tmp);
6287                 return COMPOSE_QUEUE_ERROR_NO_MSG;
6288         }
6289         folder_item_scan(queue);
6290         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6291                 g_warning("can't queue the message");
6292                 claws_unlink(tmp);
6293                 g_free(tmp);
6294                 return COMPOSE_QUEUE_ERROR_NO_MSG;
6295         }
6296         
6297         if (msgpath == NULL) {
6298                 claws_unlink(tmp);
6299                 g_free(tmp);
6300         } else
6301                 *msgpath = tmp;
6302
6303         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6304                 compose_remove_reedit_target(compose, FALSE);
6305         }
6306
6307         if ((msgnum != NULL) && (item != NULL)) {
6308                 *msgnum = num;
6309                 *item = queue;
6310         }
6311
6312         return COMPOSE_QUEUE_SUCCESS;
6313 }
6314
6315 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6316 {
6317         AttachInfo *ainfo;
6318         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6319         MimeInfo *mimepart;
6320 #ifdef G_OS_WIN32
6321         GFile *f;
6322         GFileInfo *fi;
6323         GError *error = NULL;
6324 #else
6325         GStatBuf statbuf;
6326 #endif
6327         goffset size;
6328         gchar *type, *subtype;
6329         GtkTreeModel *model;
6330         GtkTreeIter iter;
6331
6332         model = gtk_tree_view_get_model(tree_view);
6333         
6334         if (!gtk_tree_model_get_iter_first(model, &iter))
6335                 return 0;
6336         do {
6337                 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
6338                 
6339                 if (!is_file_exist(ainfo->file)) {
6340                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6341                         AlertValue val = alertpanel_full(_("Warning"), msg,
6342                                         _("Cancel sending"), _("Ignore attachment"), NULL,
6343                                         ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING);
6344                         g_free(msg);
6345                         if (val == G_ALERTDEFAULT) {
6346                                 return -1;
6347                         }
6348                         continue;
6349                 }
6350 #ifdef G_OS_WIN32
6351                 f = g_file_new_for_path(ainfo->file);
6352                 fi = g_file_query_info(f, "standard::size",
6353                                 G_FILE_QUERY_INFO_NONE, NULL, &error);
6354                 if (error != NULL) {
6355                         g_warning(error->message);
6356                         g_error_free(error);
6357                         g_object_unref(f);
6358                         return -1;
6359                 }
6360                 size = g_file_info_get_size(fi);
6361                 g_object_unref(fi);
6362                 g_object_unref(f);
6363 #else
6364                 if (g_stat(ainfo->file, &statbuf) < 0)
6365                         return -1;
6366                 size = statbuf.st_size;
6367 #endif
6368
6369                 mimepart = procmime_mimeinfo_new();
6370                 mimepart->content = MIMECONTENT_FILE;
6371                 mimepart->data.filename = g_strdup(ainfo->file);
6372                 mimepart->tmp = FALSE; /* or we destroy our attachment */
6373                 mimepart->offset = 0;
6374                 mimepart->length = size;
6375
6376                 type = g_strdup(ainfo->content_type);
6377
6378                 if (!strchr(type, '/')) {
6379                         g_free(type);
6380                         type = g_strdup("application/octet-stream");
6381                 }
6382
6383                 subtype = strchr(type, '/') + 1;
6384                 *(subtype - 1) = '\0';
6385                 mimepart->type = procmime_get_media_type(type);
6386                 mimepart->subtype = g_strdup(subtype);
6387                 g_free(type);
6388
6389                 if (mimepart->type == MIMETYPE_MESSAGE && 
6390                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6391                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
6392                 } else if (mimepart->type == MIMETYPE_TEXT) {
6393                         if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6394                                 /* Text parts with no name come from multipart/alternative
6395                                 * forwards. Make sure the recipient won't look at the 
6396                                 * original HTML part by mistake. */
6397                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6398                                 ainfo->name = g_strdup_printf(_("Original %s part"),
6399                                                                 mimepart->subtype);
6400                         }
6401                         if (ainfo->charset)
6402                                 g_hash_table_insert(mimepart->typeparameters,
6403                                                     g_strdup("charset"), g_strdup(ainfo->charset));
6404                 }
6405                 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6406                         if (mimepart->type == MIMETYPE_APPLICATION && 
6407                            !strcmp2(mimepart->subtype, "octet-stream"))
6408                                 g_hash_table_insert(mimepart->typeparameters,
6409                                                 g_strdup("name"), g_strdup(ainfo->name));
6410                         g_hash_table_insert(mimepart->dispositionparameters,
6411                                         g_strdup("filename"), g_strdup(ainfo->name));
6412                         mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6413                 }
6414
6415                 if (mimepart->type == MIMETYPE_MESSAGE
6416                     || mimepart->type == MIMETYPE_MULTIPART)
6417                         ainfo->encoding = ENC_BINARY;
6418                 else if (compose->use_signing) {
6419                         if (ainfo->encoding == ENC_7BIT)
6420                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6421                         else if (ainfo->encoding == ENC_8BIT)
6422                                 ainfo->encoding = ENC_BASE64;
6423                 }
6424
6425                 procmime_encode_content(mimepart, ainfo->encoding);
6426
6427                 g_node_append(parent->node, mimepart->node);
6428         } while (gtk_tree_model_iter_next(model, &iter));
6429         
6430         return 0;
6431 }
6432
6433 static gchar *compose_quote_list_of_addresses(gchar *str)
6434 {
6435         GSList *list = NULL, *item = NULL;
6436         gchar *qname = NULL, *faddr = NULL, *result = NULL;
6437
6438         list = address_list_append_with_comments(list, str);
6439         for (item = list; item != NULL; item = item->next) {
6440                 gchar *spec = item->data;
6441                 gchar *endofname = strstr(spec, " <");
6442                 if (endofname != NULL) {
6443                         gchar * qqname;
6444                         *endofname = '\0';
6445                         QUOTE_IF_REQUIRED_NORMAL(qname, spec, return NULL);
6446                         qqname = escape_internal_quotes(qname, '"');
6447                         *endofname = ' ';
6448                         if (*qname != *spec || qqname != qname) { /* has been quoted, compute new */
6449                                 gchar *addr = g_strdup(endofname);
6450                                 gchar *name = (qqname != qname)? qqname: g_strdup(qname);
6451                                 faddr = g_strconcat(name, addr, NULL);
6452                                 g_free(name);
6453                                 g_free(addr);
6454                                 debug_print("new auto-quoted address: '%s'\n", faddr);
6455                         }
6456                 }
6457                 if (result == NULL)
6458                         result = g_strdup((faddr != NULL)? faddr: spec);
6459                 else {
6460                         result = g_strconcat(result,
6461                                              ", ",
6462                                              (faddr != NULL)? faddr: spec,
6463                                              NULL);
6464                 }
6465                 if (faddr != NULL) {
6466                         g_free(faddr);
6467                         faddr = NULL;
6468                 }
6469         }
6470         slist_free_strings_full(list);
6471
6472         return result;
6473 }
6474
6475 #define IS_IN_CUSTOM_HEADER(header) \
6476         (compose->account->add_customhdr && \
6477          custom_header_find(compose->account->customhdr_list, header) != NULL)
6478
6479 static const gchar *compose_untranslated_header_name(gchar *header_name)
6480 {
6481         /* return the untranslated header name, if header_name is a known
6482            header name, in either its translated or untranslated form, with
6483            or without trailing colon. otherwise, returns header_name. */
6484         gchar *translated_header_name;
6485         gchar *translated_header_name_wcolon;
6486         const gchar *untranslated_header_name;
6487         const gchar *untranslated_header_name_wcolon;
6488         gint i;
6489
6490         cm_return_val_if_fail(header_name != NULL, NULL);
6491
6492         for (i = 0; HEADERS[i].header_name != NULL; i++) {
6493                 untranslated_header_name = HEADERS[i].header_name;
6494                 untranslated_header_name_wcolon = HEADERS[i].header_name_w_colon;
6495
6496                 translated_header_name = gettext(untranslated_header_name);
6497                 translated_header_name_wcolon = gettext(untranslated_header_name_wcolon);
6498
6499                 if (!strcmp(header_name, untranslated_header_name) ||
6500                         !strcmp(header_name, translated_header_name)) {
6501                         return untranslated_header_name;
6502                 } else {
6503                         if (!strcmp(header_name, untranslated_header_name_wcolon) ||
6504                                 !strcmp(header_name, translated_header_name_wcolon)) {
6505                                 return untranslated_header_name_wcolon;
6506                         }
6507                 }
6508         }
6509         debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name);
6510         return header_name;
6511 }
6512
6513 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6514                                                     GString *header, 
6515                                                     const gchar *fieldname,
6516                                                     const gchar *seperator)
6517 {
6518         gchar *str, *fieldname_w_colon;
6519         gboolean add_field = FALSE;
6520         GSList *list;
6521         ComposeHeaderEntry *headerentry;
6522         const gchar *headerentryname;
6523         const gchar *trans_fieldname;
6524         GString *fieldstr;
6525
6526         if (IS_IN_CUSTOM_HEADER(fieldname))
6527                 return;
6528
6529         debug_print("Adding %s-fields\n", fieldname);
6530
6531         fieldstr = g_string_sized_new(64);
6532
6533         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6534         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6535
6536         for (list = compose->header_list; list; list = list->next) {
6537                 headerentry = ((ComposeHeaderEntry *)list->data);
6538                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6539
6540                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6541                         gchar * ustr = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6542                         g_strstrip(ustr);
6543                         str = compose_quote_list_of_addresses(ustr);
6544                         g_free(ustr);
6545                         if (str != NULL && str[0] != '\0') {
6546                                 if (add_field)
6547                                         g_string_append(fieldstr, seperator);
6548                                 g_string_append(fieldstr, str);
6549                                 add_field = TRUE;
6550                         }
6551                         g_free(str);
6552                 }
6553         }
6554         if (add_field) {
6555                 gchar *buf;
6556
6557                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6558                 compose_convert_header
6559                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6560                         strlen(fieldname) + 2, TRUE);
6561                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6562                 g_free(buf);
6563         }
6564
6565         g_free(fieldname_w_colon);
6566         g_string_free(fieldstr, TRUE);
6567
6568         return;
6569 }
6570
6571 static gchar *compose_get_manual_headers_info(Compose *compose)
6572 {
6573         GString *sh_header = g_string_new(" ");
6574         GSList *list;
6575         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6576
6577         for (list = compose->header_list; list; list = list->next) {
6578                 ComposeHeaderEntry *headerentry;
6579                 gchar *tmp;
6580                 gchar *headername;
6581                 gchar *headername_wcolon;
6582                 const gchar *headername_trans;
6583                 gchar **string;
6584                 gboolean standard_header = FALSE;
6585
6586                 headerentry = ((ComposeHeaderEntry *)list->data);
6587
6588                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6589                 g_strstrip(tmp);
6590                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6591                         g_free(tmp);
6592                         continue;
6593                 }
6594
6595                 if (!strstr(tmp, ":")) {
6596                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6597                         headername = g_strdup(tmp);
6598                 } else {
6599                         headername_wcolon = g_strdup(tmp);
6600                         headername = g_strdup(strtok(tmp, ":"));
6601                 }
6602                 g_free(tmp);
6603                 
6604                 string = std_headers;
6605                 while (*string != NULL) {
6606                         headername_trans = prefs_common_translated_header_name(*string);
6607                         if (!strcmp(headername_trans, headername_wcolon))
6608                                 standard_header = TRUE;
6609                         string++;
6610                 }
6611                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6612                         g_string_append_printf(sh_header, "%s ", headername);
6613                 g_free(headername);
6614                 g_free(headername_wcolon);
6615         }
6616         g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6617         return g_string_free(sh_header, FALSE);
6618 }
6619
6620 static gchar *compose_get_header(Compose *compose)
6621 {
6622         gchar date[RFC822_DATE_BUFFSIZE];
6623         gchar buf[BUFFSIZE];
6624         const gchar *entry_str;
6625         gchar *str;
6626         gchar *name;
6627         GSList *list;
6628         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6629         GString *header;
6630         gchar *from_name = NULL, *from_address = NULL;
6631         gchar *tmp;
6632
6633         cm_return_val_if_fail(compose->account != NULL, NULL);
6634         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6635
6636         header = g_string_sized_new(64);
6637
6638         /* Date */
6639         if (prefs_common.hide_timezone)
6640                 get_rfc822_date_hide_tz(date, sizeof(date));
6641         else
6642                 get_rfc822_date(date, sizeof(date));
6643         g_string_append_printf(header, "Date: %s\n", date);
6644
6645         /* From */
6646         
6647         if (compose->account->name && *compose->account->name) {
6648                 gchar *buf;
6649                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6650                 tmp = g_strdup_printf("%s <%s>",
6651                         buf, compose->account->address);
6652         } else {
6653                 tmp = g_strdup_printf("%s",
6654                         compose->account->address);
6655         }
6656         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6657         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6658                 /* use default */
6659                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6660                 from_address = g_strdup(compose->account->address);
6661         } else {
6662                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6663                 /* extract name and address */
6664                 if (strstr(spec, " <") && strstr(spec, ">")) {
6665                         from_address = g_strdup(strrchr(spec, '<')+1);
6666                         *(strrchr(from_address, '>')) = '\0';
6667                         from_name = g_strdup(spec);
6668                         *(strrchr(from_name, '<')) = '\0';
6669                 } else {
6670                         from_name = NULL;
6671                         from_address = g_strdup(spec);
6672                 }
6673                 g_free(spec);
6674         }
6675         g_free(tmp);
6676         
6677         
6678         if (from_name && *from_name) {
6679                 gchar *qname;
6680                 compose_convert_header
6681                         (compose, buf, sizeof(buf), from_name,
6682                          strlen("From: "), TRUE);
6683                 QUOTE_IF_REQUIRED(name, buf);
6684                 qname = escape_internal_quotes(name, '"');
6685                 
6686                 g_string_append_printf(header, "From: %s <%s>\n",
6687                         qname, from_address);
6688                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6689                     compose->return_receipt) {
6690                         compose_convert_header(compose, buf, sizeof(buf), from_name,
6691                                                strlen("Disposition-Notification-To: "),
6692                                                TRUE);
6693                         g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, from_address);
6694                 }
6695                 if (qname != name)
6696                         g_free(qname);
6697         } else {
6698                 g_string_append_printf(header, "From: %s\n", from_address);
6699                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6700                     compose->return_receipt)
6701                         g_string_append_printf(header, "Disposition-Notification-To: %s\n", from_address);
6702
6703         }
6704         g_free(from_name);
6705         g_free(from_address);
6706
6707         /* To */
6708         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6709
6710         /* Newsgroups */
6711         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6712
6713         /* Cc */
6714         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6715
6716         /* Bcc */
6717         /* 
6718          * If this account is a NNTP account remove Bcc header from 
6719          * message body since it otherwise will be publicly shown
6720          */
6721         if (compose->account->protocol != A_NNTP)
6722                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6723
6724         /* Subject */
6725         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6726
6727         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6728                 g_strstrip(str);
6729                 if (*str != '\0') {
6730                         compose_convert_header(compose, buf, sizeof(buf), str,
6731                                                strlen("Subject: "), FALSE);
6732                         g_string_append_printf(header, "Subject: %s\n", buf);
6733                 }
6734         }
6735         g_free(str);
6736
6737         /* Message-ID */
6738         if (compose->msgid != NULL && strlen(compose->msgid) > 0) {
6739                 g_string_append_printf(header, "Message-ID: <%s>\n",
6740                                 compose->msgid);
6741         }
6742
6743         if (compose->remove_references == FALSE) {
6744                 /* In-Reply-To */
6745                 if (compose->inreplyto && compose->to_list)
6746                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6747         
6748                 /* References */
6749                 if (compose->references)
6750                         g_string_append_printf(header, "References: %s\n", compose->references);
6751         }
6752
6753         /* Followup-To */
6754         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6755
6756         /* Reply-To */
6757         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6758
6759         /* Organization */
6760         if (compose->account->organization &&
6761             strlen(compose->account->organization) &&
6762             !IS_IN_CUSTOM_HEADER("Organization")) {
6763                 compose_convert_header(compose, buf, sizeof(buf),
6764                                        compose->account->organization,
6765                                        strlen("Organization: "), FALSE);
6766                 g_string_append_printf(header, "Organization: %s\n", buf);
6767         }
6768
6769         /* Program version and system info */
6770         if (compose->account->gen_xmailer &&
6771             g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6772             !compose->newsgroup_list) {
6773                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6774                         prog_version,
6775                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6776                         TARGET_ALIAS);
6777         }
6778         if (compose->account->gen_xmailer &&
6779             g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6780                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6781                         prog_version,
6782                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6783                         TARGET_ALIAS);
6784         }
6785
6786         /* custom headers */
6787         if (compose->account->add_customhdr) {
6788                 GSList *cur;
6789
6790                 for (cur = compose->account->customhdr_list; cur != NULL;
6791                      cur = cur->next) {
6792                         CustomHeader *chdr = (CustomHeader *)cur->data;
6793
6794                         if (custom_header_is_allowed(chdr->name)
6795                             && chdr->value != NULL
6796                             && *(chdr->value) != '\0') {
6797                                 compose_convert_header
6798                                         (compose, buf, sizeof(buf),
6799                                          chdr->value,
6800                                          strlen(chdr->name) + 2, FALSE);
6801                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6802                         }
6803                 }
6804         }
6805
6806         /* Automatic Faces and X-Faces */
6807         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6808                 g_string_append_printf(header, "X-Face: %s\n", buf);
6809         }
6810         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6811                 g_string_append_printf(header, "X-Face: %s\n", buf);
6812         }
6813         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6814                 g_string_append_printf(header, "Face: %s\n", buf);
6815         }
6816         else if (get_default_face (buf, sizeof(buf)) == 0) {
6817                 g_string_append_printf(header, "Face: %s\n", buf);
6818         }
6819
6820         /* PRIORITY */
6821         switch (compose->priority) {
6822                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6823                                                    "X-Priority: 1 (Highest)\n");
6824                         break;
6825                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6826                                                 "X-Priority: 2 (High)\n");
6827                         break;
6828                 case PRIORITY_NORMAL: break;
6829                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6830                                                "X-Priority: 4 (Low)\n");
6831                         break;
6832                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6833                                                   "X-Priority: 5 (Lowest)\n");
6834                         break;
6835                 default: debug_print("compose: priority unknown : %d\n",
6836                                      compose->priority);
6837         }
6838
6839         /* get special headers */
6840         for (list = compose->header_list; list; list = list->next) {
6841                 ComposeHeaderEntry *headerentry;
6842                 gchar *tmp;
6843                 gchar *headername;
6844                 gchar *headername_wcolon;
6845                 const gchar *headername_trans;
6846                 gchar *headervalue;
6847                 gchar **string;
6848                 gboolean standard_header = FALSE;
6849
6850                 headerentry = ((ComposeHeaderEntry *)list->data);
6851
6852                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6853                 g_strstrip(tmp);
6854                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6855                         g_free(tmp);
6856                         continue;
6857                 }
6858
6859                 if (!strstr(tmp, ":")) {
6860                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6861                         headername = g_strdup(tmp);
6862                 } else {
6863                         headername_wcolon = g_strdup(tmp);
6864                         headername = g_strdup(strtok(tmp, ":"));
6865                 }
6866                 g_free(tmp);
6867                 
6868                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6869                 Xstrdup_a(headervalue, entry_str, return NULL);
6870                 subst_char(headervalue, '\r', ' ');
6871                 subst_char(headervalue, '\n', ' ');
6872                 g_strstrip(headervalue);
6873                 if (*headervalue != '\0') {
6874                         string = std_headers;
6875                         while (*string != NULL && !standard_header) {
6876                                 headername_trans = prefs_common_translated_header_name(*string);
6877                                 /* support mixed translated and untranslated headers */
6878                                 if (!strcmp(headername_trans, headername_wcolon) || !strcmp(*string, headername_wcolon))
6879                                         standard_header = TRUE;
6880                                 string++;
6881                         }
6882                         if (!standard_header && !IS_IN_CUSTOM_HEADER(headername)) {
6883                                 /* store untranslated header name */
6884                                 g_string_append_printf(header, "%s %s\n",
6885                                                 compose_untranslated_header_name(headername_wcolon), headervalue);
6886                         }
6887                 }                               
6888                 g_free(headername);
6889                 g_free(headername_wcolon);              
6890         }
6891
6892         str = header->str;
6893         g_string_free(header, FALSE);
6894
6895         return str;
6896 }
6897
6898 #undef IS_IN_CUSTOM_HEADER
6899
6900 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6901                                    gint header_len, gboolean addr_field)
6902 {
6903         gchar *tmpstr = NULL;
6904         const gchar *out_codeset = NULL;
6905
6906         cm_return_if_fail(src != NULL);
6907         cm_return_if_fail(dest != NULL);
6908
6909         if (len < 1) return;
6910
6911         tmpstr = g_strdup(src);
6912
6913         subst_char(tmpstr, '\n', ' ');
6914         subst_char(tmpstr, '\r', ' ');
6915         g_strchomp(tmpstr);
6916
6917         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6918                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6919                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6920                 g_free(tmpstr);
6921                 tmpstr = mybuf;
6922         }
6923
6924         codeconv_set_strict(TRUE);
6925         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6926                 conv_get_charset_str(compose->out_encoding));
6927         codeconv_set_strict(FALSE);
6928         
6929         if (!dest || *dest == '\0') {
6930                 gchar *test_conv_global_out = NULL;
6931                 gchar *test_conv_reply = NULL;
6932
6933                 /* automatic mode. be automatic. */
6934                 codeconv_set_strict(TRUE);
6935
6936                 out_codeset = conv_get_outgoing_charset_str();
6937                 if (out_codeset) {
6938                         debug_print("trying to convert to %s\n", out_codeset);
6939                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6940                 }
6941
6942                 if (!test_conv_global_out && compose->orig_charset
6943                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6944                         out_codeset = compose->orig_charset;
6945                         debug_print("failure; trying to convert to %s\n", out_codeset);
6946                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6947                 }
6948
6949                 if (!test_conv_global_out && !test_conv_reply) {
6950                         /* we're lost */
6951                         out_codeset = CS_INTERNAL;
6952                         debug_print("finally using %s\n", out_codeset);
6953                 }
6954                 g_free(test_conv_global_out);
6955                 g_free(test_conv_reply);
6956                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6957                                         out_codeset);
6958                 codeconv_set_strict(FALSE);
6959         }
6960         g_free(tmpstr);
6961 }
6962
6963 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6964 {
6965         gchar *address;
6966
6967         cm_return_if_fail(user_data != NULL);
6968
6969         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6970         g_strstrip(address);
6971         if (*address != '\0') {
6972                 gchar *name = procheader_get_fromname(address);
6973                 extract_address(address);
6974 #ifndef USE_ALT_ADDRBOOK
6975                 addressbook_add_contact(name, address, NULL, NULL);
6976 #else
6977                 debug_print("%s: %s\n", name, address);
6978                 if (addressadd_selection(name, address, NULL, NULL)) {
6979                         debug_print( "addressbook_add_contact - added\n" );
6980                 }
6981 #endif
6982         }
6983         g_free(address);
6984 }
6985
6986 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6987 {
6988         GtkWidget *menuitem;
6989         gchar *address;
6990
6991         cm_return_if_fail(menu != NULL);
6992         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6993
6994         menuitem = gtk_separator_menu_item_new();
6995         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6996         gtk_widget_show(menuitem);
6997
6998         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6999         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
7000
7001         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
7002         g_strstrip(address);
7003         if (*address == '\0') {
7004                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
7005         }
7006
7007         g_signal_connect(G_OBJECT(menuitem), "activate",
7008                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
7009         gtk_widget_show(menuitem);
7010 }
7011
7012 void compose_add_extra_header(gchar *header, GtkListStore *model)
7013 {
7014         GtkTreeIter iter;
7015         if (strcmp(header, "")) {
7016                 COMBOBOX_ADD(model, header, COMPOSE_TO);
7017         }
7018 }
7019
7020 void compose_add_extra_header_entries(GtkListStore *model)
7021 {
7022         FILE *exh;
7023         gchar *exhrc;
7024         gchar buf[BUFFSIZE];
7025         gint lastc;
7026
7027         if (extra_headers == NULL) {
7028                 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
7029                 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
7030                         debug_print("extra headers file not found\n");
7031                         goto extra_headers_done;
7032                 }
7033                 while (fgets(buf, BUFFSIZE, exh) != NULL) {
7034                         lastc = strlen(buf) - 1;        /* remove trailing control chars */
7035                         while (lastc >= 0 && buf[lastc] != ':')
7036                                 buf[lastc--] = '\0';
7037                         if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
7038                                 buf[lastc] = '\0'; /* remove trailing : for comparison */
7039                                 if (custom_header_is_allowed(buf)) {
7040                                         buf[lastc] = ':';
7041                                         extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
7042                                 }
7043                                 else
7044                                         g_message("disallowed extra header line: %s\n", buf);
7045                         }
7046                         else {
7047                                 if (buf[0] != '#')
7048                                         g_message("invalid extra header line: %s\n", buf);
7049                         }
7050                 }
7051                 fclose(exh);
7052 extra_headers_done:
7053                 g_free(exhrc);
7054                 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
7055                 extra_headers = g_slist_reverse(extra_headers);
7056         }
7057         g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
7058 }
7059
7060 #ifdef USE_LDAP
7061 static void _ldap_srv_func(gpointer data, gpointer user_data)
7062 {
7063         LdapServer *server = (LdapServer *)data;
7064         gboolean *enable = (gboolean *)user_data;
7065
7066         debug_print("%s server '%s'\n", (*enable == TRUE ? "enabling" : "disabling"), server->control->hostName);
7067         server->searchFlag = *enable;
7068 }
7069 #endif
7070
7071 static void compose_create_header_entry(Compose *compose) 
7072 {
7073         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
7074
7075         GtkWidget *combo;
7076         GtkWidget *entry;
7077         GtkWidget *button;
7078         GtkWidget *hbox;
7079         gchar **string;
7080         const gchar *header = NULL;
7081         ComposeHeaderEntry *headerentry;
7082         gboolean standard_header = FALSE;
7083         GtkListStore *model;
7084         GtkTreeIter iter;
7085         
7086         headerentry = g_new0(ComposeHeaderEntry, 1);
7087
7088         /* Combo box model */
7089         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
7090         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
7091                         COMPOSE_TO);
7092         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
7093                         COMPOSE_CC);
7094         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
7095                         COMPOSE_BCC);
7096         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
7097                         COMPOSE_NEWSGROUPS);                    
7098         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
7099                         COMPOSE_REPLYTO);
7100         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
7101                         COMPOSE_FOLLOWUPTO);
7102         compose_add_extra_header_entries(model);
7103
7104         /* Combo box */
7105         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
7106         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
7107         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
7108         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
7109         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
7110         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
7111         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
7112                          G_CALLBACK(compose_grab_focus_cb), compose);
7113         gtk_widget_show(combo);
7114
7115         /* Putting only the combobox child into focus chain of its parent causes
7116          * the parent to be skipped when changing focus via Tab or Shift+Tab.
7117          * This eliminates need to pres Tab twice in order to really get from the
7118          * combobox to next widget. */
7119         GList *l = NULL;
7120         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
7121         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
7122         g_list_free(l);
7123
7124         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
7125                         compose->header_nextrow, compose->header_nextrow+1,
7126                         GTK_SHRINK, GTK_FILL, 0, 0);
7127         if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
7128                 const gchar *last_header_entry = gtk_entry_get_text(
7129                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
7130                 string = headers;
7131                 while (*string != NULL) {
7132                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
7133                                 standard_header = TRUE;
7134                         string++;
7135                 }
7136                 if (standard_header)
7137                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
7138         }
7139         if (!compose->header_last || !standard_header) {
7140                 switch(compose->account->protocol) {
7141                         case A_NNTP:
7142                                 header = prefs_common_translated_header_name("Newsgroups:");
7143                                 break;
7144                         default:
7145                                 header = prefs_common_translated_header_name("To:");
7146                                 break;
7147                 }                                                                   
7148         }
7149         if (header)
7150                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
7151
7152         gtk_editable_set_editable(
7153                 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo)))),
7154                 prefs_common.type_any_header);
7155
7156         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
7157                          G_CALLBACK(compose_grab_focus_cb), compose);
7158
7159         /* Entry field with cleanup button */
7160         button = gtk_button_new();
7161         gtk_button_set_image(GTK_BUTTON(button),
7162                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
7163         gtk_widget_show(button);
7164         CLAWS_SET_TIP(button,
7165                 _("Delete entry contents"));
7166         entry = gtk_entry_new(); 
7167         gtk_widget_show(entry);
7168         CLAWS_SET_TIP(entry,
7169                 _("Use <tab> to autocomplete from addressbook"));
7170         hbox = gtk_hbox_new (FALSE, 0);
7171         gtk_widget_show(hbox);
7172         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
7173         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
7174         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
7175                         compose->header_nextrow, compose->header_nextrow+1,
7176                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
7177
7178         g_signal_connect(G_OBJECT(entry), "key-press-event", 
7179                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
7180                          headerentry);
7181         g_signal_connect(G_OBJECT(entry), "changed", 
7182                          G_CALLBACK(compose_headerentry_changed_cb), 
7183                          headerentry);
7184         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
7185                          G_CALLBACK(compose_grab_focus_cb), compose);
7186
7187         g_signal_connect(G_OBJECT(button), "clicked",
7188                          G_CALLBACK(compose_headerentry_button_clicked_cb),
7189                          headerentry); 
7190                          
7191         /* email dnd */
7192         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7193                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7194                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7195         g_signal_connect(G_OBJECT(entry), "drag_data_received",
7196                          G_CALLBACK(compose_header_drag_received_cb),
7197                          entry);
7198         g_signal_connect(G_OBJECT(entry), "drag-drop",
7199                          G_CALLBACK(compose_drag_drop),
7200                          compose);
7201         g_signal_connect(G_OBJECT(entry), "populate-popup",
7202                          G_CALLBACK(compose_entry_popup_extend),
7203                          NULL);
7204
7205 #ifdef USE_LDAP
7206 #ifndef PASSWORD_CRYPTO_OLD
7207         GSList *pwd_servers = addrindex_get_password_protected_ldap_servers();
7208         if (pwd_servers != NULL && master_passphrase() == NULL) {
7209                 gboolean enable = FALSE;
7210                 debug_print("Master passphrase not available, disabling password-protected LDAP servers for this compose window.\n");
7211                 /* Temporarily disable password-protected LDAP servers,
7212                  * because user did not provide a master passphrase.
7213                  * We can safely enable searchFlag on all servers in this list
7214                  * later, since addrindex_get_password_protected_ldap_servers()
7215                  * includes servers which have it enabled initially. */
7216                 g_slist_foreach(pwd_servers, _ldap_srv_func, &enable);
7217                 compose->passworded_ldap_servers = pwd_servers;
7218         }
7219 #endif /* PASSWORD_CRYPTO_OLD */
7220 #endif /* USE_LDAP */
7221
7222         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
7223
7224         headerentry->compose = compose;
7225         headerentry->combo = combo;
7226         headerentry->entry = entry;
7227         headerentry->button = button;
7228         headerentry->hbox = hbox;
7229         headerentry->headernum = compose->header_nextrow;
7230         headerentry->type = PREF_NONE;
7231
7232         compose->header_nextrow++;
7233         compose->header_last = headerentry;             
7234         compose->header_list =
7235                 g_slist_append(compose->header_list,
7236                                headerentry);
7237 }
7238
7239 static void compose_add_header_entry(Compose *compose, const gchar *header,
7240                                 gchar *text, ComposePrefType pref_type) 
7241 {
7242         ComposeHeaderEntry *last_header = compose->header_last;
7243         gchar *tmp = g_strdup(text), *email;
7244         gboolean replyto_hdr;
7245         
7246         replyto_hdr = (!strcasecmp(header,
7247                                 prefs_common_translated_header_name("Reply-To:")) ||
7248                         !strcasecmp(header,
7249                                 prefs_common_translated_header_name("Followup-To:")) ||
7250                         !strcasecmp(header,
7251                                 prefs_common_translated_header_name("In-Reply-To:")));
7252                 
7253         extract_address(tmp);
7254         email = g_utf8_strdown(tmp, -1);
7255         
7256         if (replyto_hdr == FALSE &&
7257             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
7258         {
7259                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7260                                 header, text, (gint) pref_type);
7261                 g_free(email);
7262                 g_free(tmp);
7263                 return;
7264         }
7265         
7266         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
7267                 gtk_entry_set_text(GTK_ENTRY(
7268                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
7269         else
7270                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
7271         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
7272         last_header->type = pref_type;
7273
7274         if (replyto_hdr == FALSE)
7275                 g_hash_table_insert(compose->email_hashtable, email,
7276                                     GUINT_TO_POINTER(1));
7277         else
7278                 g_free(email);
7279         
7280         g_free(tmp);
7281 }
7282
7283 static void compose_destroy_headerentry(Compose *compose, 
7284                                         ComposeHeaderEntry *headerentry)
7285 {
7286         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7287         gchar *email;
7288
7289         extract_address(text);
7290         email = g_utf8_strdown(text, -1);
7291         g_hash_table_remove(compose->email_hashtable, email);
7292         g_free(text);
7293         g_free(email);
7294         
7295         gtk_widget_destroy(headerentry->combo);
7296         gtk_widget_destroy(headerentry->entry);
7297         gtk_widget_destroy(headerentry->button);
7298         gtk_widget_destroy(headerentry->hbox);
7299         g_free(headerentry);
7300 }
7301
7302 static void compose_remove_header_entries(Compose *compose) 
7303 {
7304         GSList *list;
7305         for (list = compose->header_list; list; list = list->next)
7306                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7307
7308         compose->header_last = NULL;
7309         g_slist_free(compose->header_list);
7310         compose->header_list = NULL;
7311         compose->header_nextrow = 1;
7312         compose_create_header_entry(compose);
7313 }
7314
7315 static GtkWidget *compose_create_header(Compose *compose) 
7316 {
7317         GtkWidget *from_optmenu_hbox;
7318         GtkWidget *header_table_main;
7319         GtkWidget *header_scrolledwin;
7320         GtkWidget *header_table;
7321
7322         /* parent with account selection and from header */
7323         header_table_main = gtk_table_new(2, 2, FALSE);
7324         gtk_widget_show(header_table_main);
7325         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7326
7327         from_optmenu_hbox = compose_account_option_menu_create(compose);
7328         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7329                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7330
7331         /* child with header labels and entries */
7332         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7333         gtk_widget_show(header_scrolledwin);
7334         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7335
7336         header_table = gtk_table_new(2, 2, FALSE);
7337         gtk_widget_show(header_table);
7338         gtk_container_set_border_width(GTK_CONTAINER(header_table), 0);
7339         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7340         gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table),
7341                         gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin)));
7342         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin))), GTK_SHADOW_NONE);
7343
7344         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7345                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7346
7347         compose->header_table = header_table;
7348         compose->header_list = NULL;
7349         compose->header_nextrow = 0;
7350
7351         compose_create_header_entry(compose);
7352
7353         compose->table = NULL;
7354
7355         return header_table_main;
7356 }
7357
7358 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7359 {
7360         Compose *compose = (Compose *)data;
7361         GdkEventButton event;
7362         
7363         event.button = 3;
7364         event.time = gtk_get_current_event_time();
7365
7366         return attach_button_pressed(compose->attach_clist, &event, compose);
7367 }
7368
7369 static GtkWidget *compose_create_attach(Compose *compose)
7370 {
7371         GtkWidget *attach_scrwin;
7372         GtkWidget *attach_clist;
7373
7374         GtkListStore *store;
7375         GtkCellRenderer *renderer;
7376         GtkTreeViewColumn *column;
7377         GtkTreeSelection *selection;
7378
7379         /* attachment list */
7380         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7381         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7382                                        GTK_POLICY_AUTOMATIC,
7383                                        GTK_POLICY_AUTOMATIC);
7384         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7385
7386         store = gtk_list_store_new(N_ATTACH_COLS, 
7387                                    G_TYPE_STRING,
7388                                    G_TYPE_STRING,
7389                                    G_TYPE_STRING,
7390                                    G_TYPE_STRING,
7391                                    G_TYPE_POINTER,
7392                                    G_TYPE_AUTO_POINTER,
7393                                    -1);
7394         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7395                                         (GTK_TREE_MODEL(store)));
7396         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7397         g_object_unref(store);
7398         
7399         renderer = gtk_cell_renderer_text_new();
7400         column = gtk_tree_view_column_new_with_attributes
7401                         (_("Mime type"), renderer, "text", 
7402                          COL_MIMETYPE, NULL);
7403         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7404         
7405         renderer = gtk_cell_renderer_text_new();
7406         column = gtk_tree_view_column_new_with_attributes
7407                         (_("Size"), renderer, "text", 
7408                          COL_SIZE, NULL);
7409         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7410         
7411         renderer = gtk_cell_renderer_text_new();
7412         column = gtk_tree_view_column_new_with_attributes
7413                         (_("Name"), renderer, "text", 
7414                          COL_NAME, NULL);
7415         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7416
7417         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7418                                      prefs_common.use_stripes_everywhere);
7419         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7420         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7421
7422         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7423                          G_CALLBACK(attach_selected), compose);
7424         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7425                          G_CALLBACK(attach_button_pressed), compose);
7426         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7427                          G_CALLBACK(popup_attach_button_pressed), compose);
7428         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7429                          G_CALLBACK(attach_key_pressed), compose);
7430
7431         /* drag and drop */
7432         gtk_drag_dest_set(attach_clist,
7433                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7434                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7435                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7436         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7437                          G_CALLBACK(compose_attach_drag_received_cb),
7438                          compose);
7439         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7440                          G_CALLBACK(compose_drag_drop),
7441                          compose);
7442
7443         compose->attach_scrwin = attach_scrwin;
7444         compose->attach_clist  = attach_clist;
7445
7446         return attach_scrwin;
7447 }
7448
7449 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7450 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7451
7452 static GtkWidget *compose_create_others(Compose *compose)
7453 {
7454         GtkWidget *table;
7455         GtkWidget *savemsg_checkbtn;
7456         GtkWidget *savemsg_combo;
7457         GtkWidget *savemsg_select;
7458         
7459         guint rowcount = 0;
7460         gchar *folderidentifier;
7461
7462         /* Table for settings */
7463         table = gtk_table_new(3, 1, FALSE);
7464         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7465         gtk_widget_show(table);
7466         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7467         rowcount = 0;
7468
7469         /* Save Message to folder */
7470         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7471         gtk_widget_show(savemsg_checkbtn);
7472         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7473         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7474                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7475         }
7476         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7477                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7478
7479         savemsg_combo = gtk_combo_box_text_new_with_entry();
7480         compose->savemsg_checkbtn = savemsg_checkbtn;
7481         compose->savemsg_combo = savemsg_combo;
7482         gtk_widget_show(savemsg_combo);
7483
7484         if (prefs_common.compose_save_to_history)
7485                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7486                                 prefs_common.compose_save_to_history);
7487         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7488         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7489         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7490                          G_CALLBACK(compose_grab_focus_cb), compose);
7491         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7492                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7493                                   (compose->account, F_OUTBOX));
7494                 compose_set_save_to(compose, folderidentifier);
7495                 g_free(folderidentifier);
7496         }
7497
7498         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7499         gtk_widget_show(savemsg_select);
7500         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7501         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7502                          G_CALLBACK(compose_savemsg_select_cb),
7503                          compose);
7504
7505         return table;   
7506 }
7507
7508 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7509 {
7510         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7511                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7512 }
7513
7514 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7515 {
7516         FolderItem *dest;
7517         gchar * path;
7518
7519         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
7520                         _("Select folder to save message to"));
7521         if (!dest) return;
7522
7523         path = folder_item_get_identifier(dest);
7524
7525         compose_set_save_to(compose, path);
7526         g_free(path);
7527 }
7528
7529 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7530                                   GdkAtom clip, GtkTextIter *insert_place);
7531
7532
7533 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7534                                        Compose *compose)
7535 {
7536         gint prev_autowrap;
7537         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7538 #if USE_ENCHANT
7539         if (event->button == 3) {
7540                 GtkTextIter iter;
7541                 GtkTextIter sel_start, sel_end;
7542                 gboolean stuff_selected;
7543                 gint x, y;
7544                 /* move the cursor to allow GtkAspell to check the word
7545                  * under the mouse */
7546                 if (event->x && event->y) {
7547                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7548                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7549                                 &x, &y);
7550                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7551                                 &iter, x, y);
7552                 } else {
7553                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7554                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7555                 }
7556                 /* get selection */
7557                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7558                                 buffer,
7559                                 &sel_start, &sel_end);
7560
7561                 gtk_text_buffer_place_cursor (buffer, &iter);
7562                 /* reselect stuff */
7563                 if (stuff_selected 
7564                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7565                         gtk_text_buffer_select_range(buffer,
7566                                 &sel_start, &sel_end);
7567                 }
7568                 return FALSE; /* pass the event so that the right-click goes through */
7569         }
7570 #endif
7571         if (event->button == 2) {
7572                 GtkTextIter iter;
7573                 gint x, y;
7574                 BLOCK_WRAP();
7575                 
7576                 /* get the middle-click position to paste at the correct place */
7577                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7578                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7579                         &x, &y);
7580                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7581                         &iter, x, y);
7582                 
7583                 entry_paste_clipboard(compose, text, 
7584                                 prefs_common.linewrap_pastes,
7585                                 GDK_SELECTION_PRIMARY, &iter);
7586                 UNBLOCK_WRAP();
7587                 return TRUE;
7588         }
7589         return FALSE;
7590 }
7591
7592 #if USE_ENCHANT
7593 static void compose_spell_menu_changed(void *data)
7594 {
7595         Compose *compose = (Compose *)data;
7596         GSList *items;
7597         GtkWidget *menuitem;
7598         GtkWidget *parent_item;
7599         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7600         GSList *spell_menu;
7601
7602         if (compose->gtkaspell == NULL)
7603                 return;
7604
7605         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7606                         "/Menu/Spelling/Options");
7607
7608         /* setting the submenu removes /Spelling/Options from the factory 
7609          * so we need to save it */
7610
7611         if (parent_item == NULL) {
7612                 parent_item = compose->aspell_options_menu;
7613                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7614         } else
7615                 compose->aspell_options_menu = parent_item;
7616
7617         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7618
7619         spell_menu = g_slist_reverse(spell_menu);
7620         for (items = spell_menu;
7621              items; items = items->next) {
7622                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7623                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7624                 gtk_widget_show(GTK_WIDGET(menuitem));
7625         }
7626         g_slist_free(spell_menu);
7627
7628         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7629         gtk_widget_show(parent_item);
7630 }
7631
7632 static void compose_dict_changed(void *data)
7633 {
7634         Compose *compose = (Compose *) data;
7635
7636         if(!compose->gtkaspell)
7637                 return; 
7638         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7639                 return;
7640
7641         gtkaspell_highlight_all(compose->gtkaspell);
7642         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7643 }
7644 #endif
7645
7646 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7647 {
7648         Compose *compose = (Compose *)data;
7649         GdkEventButton event;
7650         
7651         event.button = 3;
7652         event.time = gtk_get_current_event_time();
7653         event.x = 0;
7654         event.y = 0;
7655
7656         return text_clicked(compose->text, &event, compose);
7657 }
7658
7659 static gboolean compose_force_window_origin = TRUE;
7660 static Compose *compose_create(PrefsAccount *account,
7661                                                  FolderItem *folder,
7662                                                  ComposeMode mode,
7663                                                  gboolean batch)
7664 {
7665         Compose   *compose;
7666         GtkWidget *window;
7667         GtkWidget *vbox;
7668         GtkWidget *menubar;
7669         GtkWidget *handlebox;
7670
7671         GtkWidget *notebook;
7672         
7673         GtkWidget *attach_hbox;
7674         GtkWidget *attach_lab1;
7675         GtkWidget *attach_lab2;
7676
7677         GtkWidget *vbox2;
7678
7679         GtkWidget *label;
7680         GtkWidget *subject_hbox;
7681         GtkWidget *subject_frame;
7682         GtkWidget *subject_entry;
7683         GtkWidget *subject;
7684         GtkWidget *paned;
7685
7686         GtkWidget *edit_vbox;
7687         GtkWidget *ruler_hbox;
7688         GtkWidget *ruler;
7689         GtkWidget *scrolledwin;
7690         GtkWidget *text;
7691         GtkTextBuffer *buffer;
7692         GtkClipboard *clipboard;
7693
7694         UndoMain *undostruct;
7695
7696         GtkWidget *popupmenu;
7697         GtkWidget *tmpl_menu;
7698         GtkActionGroup *action_group = NULL;
7699
7700 #if USE_ENCHANT
7701         GtkAspell * gtkaspell = NULL;
7702 #endif
7703
7704         static GdkGeometry geometry;
7705
7706         cm_return_val_if_fail(account != NULL, NULL);
7707
7708         gtkut_convert_int_to_gdk_color(prefs_common.color[COL_DEFAULT_HEADER_BG],
7709                                            &default_header_bgcolor);
7710         gtkut_convert_int_to_gdk_color(prefs_common.color[COL_DEFAULT_HEADER],
7711                                            &default_header_color);
7712
7713         debug_print("Creating compose window...\n");
7714         compose = g_new0(Compose, 1);
7715
7716         compose->batch = batch;
7717         compose->account = account;
7718         compose->folder = folder;
7719         
7720         compose->mutex = cm_mutex_new();
7721         compose->set_cursor_pos = -1;
7722
7723         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7724
7725         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7726         gtk_widget_set_size_request(window, prefs_common.compose_width,
7727                                         prefs_common.compose_height);
7728
7729         if (!geometry.max_width) {
7730                 geometry.max_width = gdk_screen_width();
7731                 geometry.max_height = gdk_screen_height();
7732         }
7733
7734         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7735                                       &geometry, GDK_HINT_MAX_SIZE);
7736         if (!geometry.min_width) {
7737                 geometry.min_width = 600;
7738                 geometry.min_height = 440;
7739         }
7740         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7741                                       &geometry, GDK_HINT_MIN_SIZE);
7742
7743 #ifndef GENERIC_UMPC    
7744         if (compose_force_window_origin)
7745                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7746                                  prefs_common.compose_y);
7747 #endif
7748         g_signal_connect(G_OBJECT(window), "delete_event",
7749                          G_CALLBACK(compose_delete_cb), compose);
7750         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7751         gtk_widget_realize(window);
7752
7753         gtkut_widget_set_composer_icon(window);
7754
7755         vbox = gtk_vbox_new(FALSE, 0);
7756         gtk_container_add(GTK_CONTAINER(window), vbox);
7757
7758         compose->ui_manager = gtk_ui_manager_new();
7759         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7760                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7761         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7762                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7763         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7764                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7765         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7766                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7767         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7768                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7769
7770         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7771
7772         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7773         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7774 #ifdef USE_ENCHANT
7775         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7776 #endif
7777         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7778         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7779         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7780
7781 /* Compose menu */
7782         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7783         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7784         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7785         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7786         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7787         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7788         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7789         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7790         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7791         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7792         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7793         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7794         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7795
7796 /* Edit menu */
7797         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7798         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7799         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7800
7801         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7802         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7803         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7804
7805         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7806         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7807         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7808         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7809
7810         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7811
7812         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7813         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7814         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7815         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7816         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7817         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7818         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7819         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7820         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7821         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7822         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7823         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7824         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7825         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7826         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7827
7828         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7829
7830         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7831         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7832         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7833         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7834         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7835
7836         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7837
7838         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7839
7840 #if USE_ENCHANT
7841 /* Spelling menu */
7842         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7843         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7844         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7845         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7846         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7847         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7848 #endif
7849
7850 /* Options menu */
7851         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7852         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7853         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7854         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7855         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7856
7857         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7858         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7859         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7860         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7861         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7862
7863         
7864         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7865         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7866         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7867         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7868         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7869         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7870         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7871
7872         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7873         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7874         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7875         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7876         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7877
7878         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7879
7880         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7881         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7882         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7883         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7884         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7885
7886         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7887         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)
7888         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)
7889         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7890
7891         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7892
7893         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7894         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)
7895         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)
7896
7897         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7898
7899         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7900         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)
7901         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7902
7903         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7904         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)
7905         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7906
7907         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7908
7909         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7910         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)
7911         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7912         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR, "Options/Encoding/Cyrillic/"CS_MACCYR, GTK_UI_MANAGER_MENUITEM)
7913         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7914         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7915
7916         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7917         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)
7918         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)
7919         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7920         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7921
7922         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7923         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7924         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7925         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7926         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7927         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7928
7929         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7930         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7931         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)
7932
7933         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7934         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7935         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7936 /* phew. */
7937
7938 /* Tools menu */
7939         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7940         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7941         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7942         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7943         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7944         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7945
7946 /* Help menu */
7947         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7948
7949         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7950         gtk_widget_show_all(menubar);
7951
7952         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7953         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7954
7955         if (prefs_common.toolbar_detachable) {
7956                 handlebox = gtk_handle_box_new();
7957         } else {
7958                 handlebox = gtk_hbox_new(FALSE, 0);
7959         }
7960         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7961
7962         gtk_widget_realize(handlebox);
7963         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7964                                           (gpointer)compose);
7965
7966         vbox2 = gtk_vbox_new(FALSE, 2);
7967         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7968         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7969         
7970         /* Notebook */
7971         notebook = gtk_notebook_new();
7972         gtk_widget_show(notebook);
7973
7974         /* header labels and entries */
7975         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7976                         compose_create_header(compose),
7977                         gtk_label_new_with_mnemonic(_("Hea_der")));
7978         /* attachment list */
7979         attach_hbox = gtk_hbox_new(FALSE, 0);
7980         gtk_widget_show(attach_hbox);
7981         
7982         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7983         gtk_widget_show(attach_lab1);
7984         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7985         
7986         attach_lab2 = gtk_label_new("");
7987         gtk_widget_show(attach_lab2);
7988         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7989         
7990         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7991                         compose_create_attach(compose),
7992                         attach_hbox);
7993         /* Others Tab */
7994         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7995                         compose_create_others(compose),
7996                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7997
7998         /* Subject */
7999         subject_hbox = gtk_hbox_new(FALSE, 0);
8000         gtk_widget_show(subject_hbox);
8001
8002         subject_frame = gtk_frame_new(NULL);
8003         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
8004         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
8005         gtk_widget_show(subject_frame);
8006
8007         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
8008         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
8009         gtk_widget_show(subject);
8010
8011         label = gtk_label_new_with_mnemonic(_("S_ubject:"));
8012         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
8013         gtk_widget_show(label);
8014
8015 #ifdef USE_ENCHANT
8016         subject_entry = claws_spell_entry_new();
8017 #else
8018         subject_entry = gtk_entry_new();
8019 #endif
8020         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
8021         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
8022                          G_CALLBACK(compose_grab_focus_cb), compose);
8023         gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
8024         gtk_widget_show(subject_entry);
8025         compose->subject_entry = subject_entry;
8026         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
8027         
8028         edit_vbox = gtk_vbox_new(FALSE, 0);
8029
8030         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
8031
8032         /* ruler */
8033         ruler_hbox = gtk_hbox_new(FALSE, 0);
8034         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
8035
8036         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
8037         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
8038         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
8039                            BORDER_WIDTH);
8040
8041         /* text widget */
8042         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
8043         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
8044                                        GTK_POLICY_AUTOMATIC,
8045                                        GTK_POLICY_AUTOMATIC);
8046         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
8047                                             GTK_SHADOW_IN);
8048         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
8049
8050         text = gtk_text_view_new();
8051         if (prefs_common.show_compose_margin) {
8052                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
8053                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
8054         }
8055         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
8056         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
8057         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
8058         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8059         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
8060         
8061         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
8062         g_signal_connect_after(G_OBJECT(text), "size_allocate",
8063                                G_CALLBACK(compose_edit_size_alloc),
8064                                ruler);
8065         g_signal_connect(G_OBJECT(buffer), "changed",
8066                          G_CALLBACK(compose_changed_cb), compose);
8067         g_signal_connect(G_OBJECT(text), "grab_focus",
8068                          G_CALLBACK(compose_grab_focus_cb), compose);
8069         g_signal_connect(G_OBJECT(buffer), "insert_text",
8070                          G_CALLBACK(text_inserted), compose);
8071         g_signal_connect(G_OBJECT(text), "button_press_event",
8072                          G_CALLBACK(text_clicked), compose);
8073         g_signal_connect(G_OBJECT(text), "popup-menu",
8074                          G_CALLBACK(compose_popup_menu), compose);
8075         g_signal_connect(G_OBJECT(subject_entry), "changed",
8076                         G_CALLBACK(compose_changed_cb), compose);
8077         g_signal_connect(G_OBJECT(subject_entry), "activate",
8078                         G_CALLBACK(compose_subject_entry_activated), compose);
8079
8080         /* drag and drop */
8081         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
8082                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
8083                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
8084         g_signal_connect(G_OBJECT(text), "drag_data_received",
8085                          G_CALLBACK(compose_insert_drag_received_cb),
8086                          compose);
8087         g_signal_connect(G_OBJECT(text), "drag-drop",
8088                          G_CALLBACK(compose_drag_drop),
8089                          compose);
8090         g_signal_connect(G_OBJECT(text), "key-press-event",
8091                          G_CALLBACK(completion_set_focus_to_subject),
8092                          compose);
8093         gtk_widget_show_all(vbox);
8094
8095         /* pane between attach clist and text */
8096         paned = gtk_vpaned_new();
8097         gtk_container_add(GTK_CONTAINER(vbox2), paned);
8098         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
8099         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
8100         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
8101         g_signal_connect(G_OBJECT(notebook), "size_allocate",
8102                          G_CALLBACK(compose_notebook_size_alloc), paned);
8103
8104         gtk_widget_show_all(paned);
8105
8106
8107         if (prefs_common.textfont) {
8108                 PangoFontDescription *font_desc;
8109
8110                 font_desc = pango_font_description_from_string
8111                         (prefs_common.textfont);
8112                 if (font_desc) {
8113                         gtk_widget_modify_font(text, font_desc);
8114                         pango_font_description_free(font_desc);
8115                 }
8116         }
8117
8118         gtk_action_group_add_actions(action_group, compose_popup_entries,
8119                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
8120         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
8121         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
8122         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
8123         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
8124         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
8125         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
8126         
8127         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
8128
8129         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8130         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8131         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
8132
8133         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
8134
8135         undostruct = undo_init(text);
8136         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
8137                                    compose);
8138
8139         address_completion_start(window);
8140
8141         compose->window        = window;
8142         compose->vbox          = vbox;
8143         compose->menubar       = menubar;
8144         compose->handlebox     = handlebox;
8145
8146         compose->vbox2         = vbox2;
8147
8148         compose->paned = paned;
8149
8150         compose->attach_label  = attach_lab2;
8151
8152         compose->notebook      = notebook;
8153         compose->edit_vbox     = edit_vbox;
8154         compose->ruler_hbox    = ruler_hbox;
8155         compose->ruler         = ruler;
8156         compose->scrolledwin   = scrolledwin;
8157         compose->text          = text;
8158
8159         compose->focused_editable = NULL;
8160
8161         compose->popupmenu    = popupmenu;
8162
8163         compose->tmpl_menu = tmpl_menu;
8164
8165         compose->mode = mode;
8166         compose->rmode = mode;
8167
8168         compose->targetinfo = NULL;
8169         compose->replyinfo  = NULL;
8170         compose->fwdinfo    = NULL;
8171
8172         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
8173                                 g_str_equal, (GDestroyNotify) g_free, NULL);
8174         
8175         compose->replyto     = NULL;
8176         compose->cc          = NULL;
8177         compose->bcc         = NULL;
8178         compose->followup_to = NULL;
8179
8180         compose->ml_post     = NULL;
8181
8182         compose->inreplyto   = NULL;
8183         compose->references  = NULL;
8184         compose->msgid       = NULL;
8185         compose->boundary    = NULL;
8186
8187         compose->autowrap       = prefs_common.autowrap;
8188         compose->autoindent     = prefs_common.auto_indent;
8189         compose->use_signing    = FALSE;
8190         compose->use_encryption = FALSE;
8191         compose->privacy_system = NULL;
8192         compose->encdata        = NULL;
8193
8194         compose->modified = FALSE;
8195
8196         compose->return_receipt = FALSE;
8197
8198         compose->to_list        = NULL;
8199         compose->newsgroup_list = NULL;
8200
8201         compose->undostruct = undostruct;
8202
8203         compose->sig_str = NULL;
8204
8205         compose->exteditor_file    = NULL;
8206         compose->exteditor_pid     = -1;
8207         compose->exteditor_tag     = -1;
8208         compose->exteditor_socket  = NULL;
8209         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
8210
8211         compose->folder_update_callback_id =
8212                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
8213                                 compose_update_folder_hook,
8214                                 (gpointer) compose);
8215
8216 #if USE_ENCHANT
8217         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
8218         if (mode != COMPOSE_REDIRECT) {
8219                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
8220                     strcmp(prefs_common.dictionary, "")) {
8221                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
8222                                                   prefs_common.alt_dictionary,
8223                                                   conv_get_locale_charset_str(),
8224                                                   prefs_common.color[COL_MISSPELLED],
8225                                                   prefs_common.check_while_typing,
8226                                                   prefs_common.recheck_when_changing_dict,
8227                                                   prefs_common.use_alternate,
8228                                                   prefs_common.use_both_dicts,
8229                                                   GTK_TEXT_VIEW(text),
8230                                                   GTK_WINDOW(compose->window),
8231                                                   compose_dict_changed,
8232                                                   compose_spell_menu_changed,
8233                                                   compose);
8234                         if (!gtkaspell) {
8235                                 alertpanel_error(_("Spell checker could not "
8236                                                 "be started.\n%s"),
8237                                                 gtkaspell_checkers_strerror());
8238                                 gtkaspell_checkers_reset_error();
8239                         } else {
8240                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
8241                         }
8242                 }
8243         }
8244         compose->gtkaspell = gtkaspell;
8245         compose_spell_menu_changed(compose);
8246         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
8247 #endif
8248
8249         compose_select_account(compose, account, TRUE);
8250
8251         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
8252         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
8253
8254         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
8255                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8256
8257         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
8258                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8259         
8260         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
8261                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8262
8263         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
8264         if (account->protocol != A_NNTP)
8265                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8266                                 prefs_common_translated_header_name("To:"));
8267         else
8268                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8269                                 prefs_common_translated_header_name("Newsgroups:"));
8270
8271 #ifndef USE_ALT_ADDRBOOK
8272         addressbook_set_target_compose(compose);
8273 #endif  
8274         if (mode != COMPOSE_REDIRECT)
8275                 compose_set_template_menu(compose);
8276         else {
8277                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8278         }
8279
8280         compose_list = g_list_append(compose_list, compose);
8281
8282         if (!prefs_common.show_ruler)
8283                 gtk_widget_hide(ruler_hbox);
8284                 
8285         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8286
8287         /* Priority */
8288         compose->priority = PRIORITY_NORMAL;
8289         compose_update_priority_menu_item(compose);
8290
8291         compose_set_out_encoding(compose);
8292         
8293         /* Actions menu */
8294         compose_update_actions_menu(compose);
8295
8296         /* Privacy Systems menu */
8297         compose_update_privacy_systems_menu(compose);
8298
8299         activate_privacy_system(compose, account, TRUE);
8300         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8301         if (batch) {
8302                 gtk_widget_realize(window);
8303         } else {
8304                 gtk_widget_show(window);
8305         }
8306         
8307         return compose;
8308 }
8309
8310 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8311 {
8312         GList *accounts;
8313         GtkWidget *hbox;
8314         GtkWidget *optmenu;
8315         GtkWidget *optmenubox;
8316         GtkWidget *fromlabel;
8317         GtkListStore *menu;
8318         GtkTreeIter iter;
8319         GtkWidget *from_name = NULL;
8320
8321         gint num = 0, def_menu = 0;
8322         
8323         accounts = account_get_list();
8324         cm_return_val_if_fail(accounts != NULL, NULL);
8325
8326         optmenubox = gtk_event_box_new();
8327         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8328         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8329
8330         hbox = gtk_hbox_new(FALSE, 4);
8331         from_name = gtk_entry_new();
8332         
8333         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8334                          G_CALLBACK(compose_grab_focus_cb), compose);
8335         g_signal_connect_after(G_OBJECT(from_name), "activate",
8336                          G_CALLBACK(from_name_activate_cb), optmenu);
8337
8338         for (; accounts != NULL; accounts = accounts->next, num++) {
8339                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8340                 gchar *name, *from = NULL;
8341
8342                 if (ac == compose->account) def_menu = num;
8343
8344                 name = g_markup_printf_escaped("<i>%s</i>",
8345                                        ac->account_name);
8346                 
8347                 if (ac == compose->account) {
8348                         if (ac->name && *ac->name) {
8349                                 gchar *buf;
8350                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8351                                 from = g_strdup_printf("%s <%s>",
8352                                                        buf, ac->address);
8353                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8354                         } else {
8355                                 from = g_strdup_printf("%s",
8356                                                        ac->address);
8357                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8358                         }
8359                         if (cur_account != compose->account) {
8360                                 gtk_widget_modify_base(
8361                                         GTK_WIDGET(from_name),
8362                                         GTK_STATE_NORMAL, &default_header_bgcolor);
8363                                 gtk_widget_modify_text(
8364                                         GTK_WIDGET(from_name),
8365                                         GTK_STATE_NORMAL, &default_header_color);
8366                         }
8367                 }
8368                 COMBOBOX_ADD(menu, name, ac->account_id);
8369                 g_free(name);
8370                 g_free(from);
8371         }
8372
8373         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8374
8375         g_signal_connect(G_OBJECT(optmenu), "changed",
8376                         G_CALLBACK(account_activated),
8377                         compose);
8378         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8379                          G_CALLBACK(compose_entry_popup_extend),
8380                          NULL);
8381
8382         fromlabel = gtk_label_new_with_mnemonic(_("_From:"));
8383         gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel), from_name);
8384
8385         gtk_box_pack_start(GTK_BOX(hbox), fromlabel, FALSE, FALSE, 4);
8386         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8387         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8388
8389         /* Putting only the GtkEntry into focus chain of parent hbox causes
8390          * the account selector combobox next to it to be unreachable when
8391          * navigating widgets in GtkTable with up/down arrow keys.
8392          * Note: gtk_widget_set_can_focus() was not enough. */
8393         GList *l = NULL;
8394         l = g_list_prepend(l, from_name);
8395         gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8396         g_list_free(l);
8397         
8398         CLAWS_SET_TIP(optmenubox,
8399                 _("Account to use for this email"));
8400         CLAWS_SET_TIP(from_name,
8401                 _("Sender address to be used"));
8402
8403         compose->account_combo = optmenu;
8404         compose->from_name = from_name;
8405         
8406         return hbox;
8407 }
8408
8409 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8410 {
8411         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8412         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8413         Compose *compose = (Compose *) data;
8414         if (active) {
8415                 compose->priority = value;
8416         }
8417 }
8418
8419 static void compose_reply_change_mode(Compose *compose,
8420                                     ComposeMode action)
8421 {
8422         gboolean was_modified = compose->modified;
8423
8424         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8425         
8426         cm_return_if_fail(compose->replyinfo != NULL);
8427         
8428         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8429                 ml = TRUE;
8430         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8431                 followup = TRUE;
8432         if (action == COMPOSE_REPLY_TO_ALL)
8433                 all = TRUE;
8434         if (action == COMPOSE_REPLY_TO_SENDER)
8435                 sender = TRUE;
8436         if (action == COMPOSE_REPLY_TO_LIST)
8437                 ml = TRUE;
8438
8439         compose_remove_header_entries(compose);
8440         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8441         if (compose->account->set_autocc && compose->account->auto_cc)
8442                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8443
8444         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8445                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8446         
8447         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8448                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8449         compose_show_first_last_header(compose, TRUE);
8450         compose->modified = was_modified;
8451         compose_set_title(compose);
8452 }
8453
8454 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8455 {
8456         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8457         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8458         Compose *compose = (Compose *) data;
8459         
8460         if (active)
8461                 compose_reply_change_mode(compose, value);
8462 }
8463
8464 static void compose_update_priority_menu_item(Compose * compose)
8465 {
8466         GtkWidget *menuitem = NULL;
8467         switch (compose->priority) {
8468                 case PRIORITY_HIGHEST:
8469                         menuitem = gtk_ui_manager_get_widget
8470                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8471                         break;
8472                 case PRIORITY_HIGH:
8473                         menuitem = gtk_ui_manager_get_widget
8474                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8475                         break;
8476                 case PRIORITY_NORMAL:
8477                         menuitem = gtk_ui_manager_get_widget
8478                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8479                         break;
8480                 case PRIORITY_LOW:
8481                         menuitem = gtk_ui_manager_get_widget
8482                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8483                         break;
8484                 case PRIORITY_LOWEST:
8485                         menuitem = gtk_ui_manager_get_widget
8486                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8487                         break;
8488         }
8489         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8490 }       
8491
8492 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8493 {
8494         Compose *compose = (Compose *) data;
8495         gchar *systemid;
8496         gboolean can_sign = FALSE, can_encrypt = FALSE;
8497
8498         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8499
8500         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8501                 return;
8502
8503         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8504         g_free(compose->privacy_system);
8505         compose->privacy_system = NULL;
8506         g_free(compose->encdata);
8507         compose->encdata = NULL;
8508         if (systemid != NULL) {
8509                 compose->privacy_system = g_strdup(systemid);
8510
8511                 can_sign = privacy_system_can_sign(systemid);
8512                 can_encrypt = privacy_system_can_encrypt(systemid);
8513         }
8514
8515         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8516
8517         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8518         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8519         if (compose->toolbar->privacy_sign_btn != NULL) {
8520                 gtk_widget_set_sensitive(
8521                         GTK_WIDGET(compose->toolbar->privacy_sign_btn),
8522                         can_sign);
8523                 gtk_toggle_tool_button_set_active(
8524                         GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_sign_btn),
8525                         can_sign ? compose->use_signing : FALSE);
8526         }
8527         if (compose->toolbar->privacy_encrypt_btn != NULL) {
8528                 gtk_widget_set_sensitive(
8529                         GTK_WIDGET(compose->toolbar->privacy_encrypt_btn),
8530                         can_encrypt);
8531                 gtk_toggle_tool_button_set_active(
8532                         GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_encrypt_btn),
8533                         can_encrypt ? compose->use_encryption : FALSE);
8534         }
8535 }
8536
8537 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8538 {
8539         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8540         GtkWidget *menuitem = NULL;
8541         GList *children, *amenu;
8542         gboolean can_sign = FALSE, can_encrypt = FALSE;
8543         gboolean found = FALSE;
8544
8545         if (compose->privacy_system != NULL) {
8546                 gchar *systemid;
8547                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8548                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8549                 cm_return_if_fail(menuitem != NULL);
8550
8551                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8552                 amenu = children;
8553                 menuitem = NULL;
8554                 while (amenu != NULL) {
8555                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8556                         if (systemid != NULL) {
8557                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8558                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8559                                         menuitem = GTK_WIDGET(amenu->data);
8560
8561                                         can_sign = privacy_system_can_sign(systemid);
8562                                         can_encrypt = privacy_system_can_encrypt(systemid);
8563                                         found = TRUE;
8564                                         break;
8565                                 } 
8566                         } else if (strlen(compose->privacy_system) == 0 && 
8567                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8568                                         menuitem = GTK_WIDGET(amenu->data);
8569
8570                                         can_sign = FALSE;
8571                                         can_encrypt = FALSE;
8572                                         found = TRUE;
8573                                         break;
8574                         }
8575
8576                         amenu = amenu->next;
8577                 }
8578                 g_list_free(children);
8579                 if (menuitem != NULL)
8580                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8581                 
8582                 if (warn && !found && strlen(compose->privacy_system)) {
8583                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8584                                   "will not be able to sign or encrypt this message."),
8585                                   compose->privacy_system);
8586                 }
8587         } 
8588
8589         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8590         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8591         if (compose->toolbar->privacy_sign_btn != NULL) {
8592                 gtk_widget_set_sensitive(
8593                         GTK_WIDGET(compose->toolbar->privacy_sign_btn),
8594                         can_sign);
8595         }
8596         if (compose->toolbar->privacy_encrypt_btn != NULL) {
8597                 gtk_widget_set_sensitive(
8598                         GTK_WIDGET(compose->toolbar->privacy_encrypt_btn),
8599                         can_encrypt);
8600         }
8601 }
8602
8603 static void compose_set_out_encoding(Compose *compose)
8604 {
8605         CharSet out_encoding;
8606         const gchar *branch = NULL;
8607         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8608
8609         switch(out_encoding) {
8610                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8611                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8612                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8613                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8614                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8615                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8616                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8617                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8618                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8619                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8620                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8621                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8622                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8623                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8624                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8625                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8626                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8627                 case C_MACCYR: branch = "Menu/Options/Encoding/Cyrillic/" CS_MACCYR; break;
8628                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8629                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8630                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8631                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8632                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8633                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8634                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8635                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8636                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8637                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8638                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8639                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8640                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8641                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8642                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8643                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8644         }
8645         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8646 }
8647
8648 static void compose_set_template_menu(Compose *compose)
8649 {
8650         GSList *tmpl_list, *cur;
8651         GtkWidget *menu;
8652         GtkWidget *item;
8653
8654         tmpl_list = template_get_config();
8655
8656         menu = gtk_menu_new();
8657
8658         gtk_menu_set_accel_group (GTK_MENU (menu), 
8659                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8660         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8661                 Template *tmpl = (Template *)cur->data;
8662                 gchar *accel_path = NULL;
8663                 item = gtk_menu_item_new_with_label(tmpl->name);
8664                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8665                 g_signal_connect(G_OBJECT(item), "activate",
8666                                  G_CALLBACK(compose_template_activate_cb),
8667                                  compose);
8668                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8669                 gtk_widget_show(item);
8670                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8671                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8672                 g_free(accel_path);
8673         }
8674
8675         gtk_widget_show(menu);
8676         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8677 }
8678
8679 void compose_update_actions_menu(Compose *compose)
8680 {
8681         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8682 }
8683
8684 static void compose_update_privacy_systems_menu(Compose *compose)
8685 {
8686         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8687         GSList *systems, *cur;
8688         GtkWidget *widget;
8689         GtkWidget *system_none;
8690         GSList *group;
8691         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8692         GtkWidget *privacy_menu = gtk_menu_new();
8693
8694         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8695         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8696
8697         g_signal_connect(G_OBJECT(system_none), "activate",
8698                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8699
8700         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8701         gtk_widget_show(system_none);
8702
8703         systems = privacy_get_system_ids();
8704         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8705                 gchar *systemid = cur->data;
8706
8707                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8708                 widget = gtk_radio_menu_item_new_with_label(group,
8709                         privacy_system_get_name(systemid));
8710                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8711                                        g_strdup(systemid), g_free);
8712                 g_signal_connect(G_OBJECT(widget), "activate",
8713                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8714
8715                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8716                 gtk_widget_show(widget);
8717                 g_free(systemid);
8718         }
8719         g_slist_free(systems);
8720         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8721         gtk_widget_show_all(privacy_menu);
8722         gtk_widget_show_all(privacy_menuitem);
8723 }
8724
8725 void compose_reflect_prefs_all(void)
8726 {
8727         GList *cur;
8728         Compose *compose;
8729
8730         for (cur = compose_list; cur != NULL; cur = cur->next) {
8731                 compose = (Compose *)cur->data;
8732                 compose_set_template_menu(compose);
8733         }
8734 }
8735
8736 void compose_reflect_prefs_pixmap_theme(void)
8737 {
8738         GList *cur;
8739         Compose *compose;
8740
8741         for (cur = compose_list; cur != NULL; cur = cur->next) {
8742                 compose = (Compose *)cur->data;
8743                 toolbar_update(TOOLBAR_COMPOSE, compose);
8744         }
8745 }
8746
8747 static const gchar *compose_quote_char_from_context(Compose *compose)
8748 {
8749         const gchar *qmark = NULL;
8750
8751         cm_return_val_if_fail(compose != NULL, NULL);
8752
8753         switch (compose->mode) {
8754                 /* use forward-specific quote char */
8755                 case COMPOSE_FORWARD:
8756                 case COMPOSE_FORWARD_AS_ATTACH:
8757                 case COMPOSE_FORWARD_INLINE:
8758                         if (compose->folder && compose->folder->prefs &&
8759                                         compose->folder->prefs->forward_with_format)
8760                                 qmark = compose->folder->prefs->forward_quotemark;
8761                         else if (compose->account->forward_with_format)
8762                                 qmark = compose->account->forward_quotemark;
8763                         else
8764                                 qmark = prefs_common.fw_quotemark;
8765                         break;
8766
8767                 /* use reply-specific quote char in all other modes */
8768                 default:
8769                         if (compose->folder && compose->folder->prefs &&
8770                                         compose->folder->prefs->reply_with_format)
8771                                 qmark = compose->folder->prefs->reply_quotemark;
8772                         else if (compose->account->reply_with_format)
8773                                 qmark = compose->account->reply_quotemark;
8774                         else
8775                                 qmark = prefs_common.quotemark;
8776                         break;
8777         }
8778
8779         if (qmark == NULL || *qmark == '\0')
8780                 qmark = "> ";
8781
8782         return qmark;
8783 }
8784
8785 static void compose_template_apply(Compose *compose, Template *tmpl,
8786                                    gboolean replace)
8787 {
8788         GtkTextView *text;
8789         GtkTextBuffer *buffer;
8790         GtkTextMark *mark;
8791         GtkTextIter iter;
8792         const gchar *qmark;
8793         gchar *parsed_str = NULL;
8794         gint cursor_pos = 0;
8795         const gchar *err_msg = _("The body of the template has an error at line %d.");
8796         if (!tmpl) return;
8797
8798         /* process the body */
8799
8800         text = GTK_TEXT_VIEW(compose->text);
8801         buffer = gtk_text_view_get_buffer(text);
8802
8803         if (tmpl->value) {
8804                 qmark = compose_quote_char_from_context(compose);
8805
8806                 if (compose->replyinfo != NULL) {
8807
8808                         if (replace)
8809                                 gtk_text_buffer_set_text(buffer, "", -1);
8810                         mark = gtk_text_buffer_get_insert(buffer);
8811                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8812
8813                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8814                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8815
8816                 } else if (compose->fwdinfo != NULL) {
8817
8818                         if (replace)
8819                                 gtk_text_buffer_set_text(buffer, "", -1);
8820                         mark = gtk_text_buffer_get_insert(buffer);
8821                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8822
8823                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8824                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8825
8826                 } else {
8827                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8828
8829                         GtkTextIter start, end;
8830                         gchar *tmp = NULL;
8831
8832                         gtk_text_buffer_get_start_iter(buffer, &start);
8833                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8834                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8835
8836                         /* clear the buffer now */
8837                         if (replace)
8838                                 gtk_text_buffer_set_text(buffer, "", -1);
8839
8840                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8841                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8842                         procmsg_msginfo_free( &dummyinfo );
8843
8844                         g_free( tmp );
8845                 } 
8846         } else {
8847                 if (replace)
8848                         gtk_text_buffer_set_text(buffer, "", -1);
8849                 mark = gtk_text_buffer_get_insert(buffer);
8850                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8851         }       
8852
8853         if (replace && parsed_str && compose->account->auto_sig)
8854                 compose_insert_sig(compose, FALSE);
8855
8856         if (replace && parsed_str) {
8857                 gtk_text_buffer_get_start_iter(buffer, &iter);
8858                 gtk_text_buffer_place_cursor(buffer, &iter);
8859         }
8860         
8861         if (parsed_str) {
8862                 cursor_pos = quote_fmt_get_cursor_pos();
8863                 compose->set_cursor_pos = cursor_pos;
8864                 if (cursor_pos == -1)
8865                         cursor_pos = 0;
8866                 gtk_text_buffer_get_start_iter(buffer, &iter);
8867                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8868                 gtk_text_buffer_place_cursor(buffer, &iter);
8869         }
8870
8871         /* process the other fields */
8872
8873         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8874         compose_template_apply_fields(compose, tmpl);
8875         quote_fmt_reset_vartable();
8876         quote_fmtlex_destroy();
8877
8878         compose_changed_cb(NULL, compose);
8879
8880 #ifdef USE_ENCHANT
8881         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8882                 gtkaspell_highlight_all(compose->gtkaspell);
8883 #endif
8884 }
8885
8886 static void compose_template_apply_fields_error(const gchar *header)
8887 {
8888         gchar *tr;
8889         gchar *text;
8890
8891         tr = g_strdup(C_("'%s' stands for a header name",
8892                                   "Template '%s' format error."));
8893         text = g_strdup_printf(tr, prefs_common_translated_header_name(header));
8894         alertpanel_error("%s", text);
8895
8896         g_free(text);
8897         g_free(tr);
8898 }
8899
8900 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8901 {
8902         MsgInfo* dummyinfo = NULL;
8903         MsgInfo *msginfo = NULL;
8904         gchar *buf = NULL;
8905
8906         if (compose->replyinfo != NULL)
8907                 msginfo = compose->replyinfo;
8908         else if (compose->fwdinfo != NULL)
8909                 msginfo = compose->fwdinfo;
8910         else {
8911                 dummyinfo = compose_msginfo_new_from_compose(compose);
8912                 msginfo = dummyinfo;
8913         }
8914
8915         if (tmpl->from && *tmpl->from != '\0') {
8916 #ifdef USE_ENCHANT
8917                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8918                                 compose->gtkaspell);
8919 #else
8920                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8921 #endif
8922                 quote_fmt_scan_string(tmpl->from);
8923                 quote_fmt_parse();
8924
8925                 buf = quote_fmt_get_buffer();
8926                 if (buf == NULL) {
8927                         compose_template_apply_fields_error("From");
8928                 } else {
8929                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8930                 }
8931
8932                 quote_fmt_reset_vartable();
8933                 quote_fmtlex_destroy();
8934         }
8935
8936         if (tmpl->to && *tmpl->to != '\0') {
8937 #ifdef USE_ENCHANT
8938                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8939                                 compose->gtkaspell);
8940 #else
8941                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8942 #endif
8943                 quote_fmt_scan_string(tmpl->to);
8944                 quote_fmt_parse();
8945
8946                 buf = quote_fmt_get_buffer();
8947                 if (buf == NULL) {
8948                         compose_template_apply_fields_error("To");
8949                 } else {
8950                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8951                 }
8952
8953                 quote_fmt_reset_vartable();
8954                 quote_fmtlex_destroy();
8955         }
8956
8957         if (tmpl->cc && *tmpl->cc != '\0') {
8958 #ifdef USE_ENCHANT
8959                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8960                                 compose->gtkaspell);
8961 #else
8962                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8963 #endif
8964                 quote_fmt_scan_string(tmpl->cc);
8965                 quote_fmt_parse();
8966
8967                 buf = quote_fmt_get_buffer();
8968                 if (buf == NULL) {
8969                         compose_template_apply_fields_error("Cc");
8970                 } else {
8971                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8972                 }
8973
8974                 quote_fmt_reset_vartable();
8975                 quote_fmtlex_destroy();
8976         }
8977
8978         if (tmpl->bcc && *tmpl->bcc != '\0') {
8979 #ifdef USE_ENCHANT
8980                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8981                                 compose->gtkaspell);
8982 #else
8983                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8984 #endif
8985                 quote_fmt_scan_string(tmpl->bcc);
8986                 quote_fmt_parse();
8987
8988                 buf = quote_fmt_get_buffer();
8989                 if (buf == NULL) {
8990                         compose_template_apply_fields_error("Bcc");
8991                 } else {
8992                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8993                 }
8994
8995                 quote_fmt_reset_vartable();
8996                 quote_fmtlex_destroy();
8997         }
8998
8999         if (tmpl->replyto && *tmpl->replyto != '\0') {
9000 #ifdef USE_ENCHANT
9001                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
9002                                 compose->gtkaspell);
9003 #else
9004                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
9005 #endif
9006                 quote_fmt_scan_string(tmpl->replyto);
9007                 quote_fmt_parse();
9008
9009                 buf = quote_fmt_get_buffer();
9010                 if (buf == NULL) {
9011                         compose_template_apply_fields_error("Reply-To");
9012                 } else {
9013                         compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
9014                 }
9015
9016                 quote_fmt_reset_vartable();
9017                 quote_fmtlex_destroy();
9018         }
9019
9020         /* process the subject */
9021         if (tmpl->subject && *tmpl->subject != '\0') {
9022 #ifdef USE_ENCHANT
9023                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
9024                                 compose->gtkaspell);
9025 #else
9026                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
9027 #endif
9028                 quote_fmt_scan_string(tmpl->subject);
9029                 quote_fmt_parse();
9030
9031                 buf = quote_fmt_get_buffer();
9032                 if (buf == NULL) {
9033                         compose_template_apply_fields_error("Subject");
9034                 } else {
9035                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
9036                 }
9037
9038                 quote_fmt_reset_vartable();
9039                 quote_fmtlex_destroy();
9040         }
9041
9042         procmsg_msginfo_free( &dummyinfo );
9043 }
9044
9045 static void compose_destroy(Compose *compose)
9046 {
9047         GtkAllocation allocation;
9048         GtkTextBuffer *buffer;
9049         GtkClipboard *clipboard;
9050
9051         compose_list = g_list_remove(compose_list, compose);
9052
9053 #ifdef USE_LDAP
9054         gboolean enable = TRUE;
9055         g_slist_foreach(compose->passworded_ldap_servers,
9056                         _ldap_srv_func, &enable);
9057         g_slist_free(compose->passworded_ldap_servers);
9058 #endif
9059
9060         if (compose->updating) {
9061                 debug_print("danger, not destroying anything now\n");
9062                 compose->deferred_destroy = TRUE;
9063                 return;
9064         }
9065
9066         /* NOTE: address_completion_end() does nothing with the window
9067          * however this may change. */
9068         address_completion_end(compose->window);
9069
9070         slist_free_strings_full(compose->to_list);
9071         slist_free_strings_full(compose->newsgroup_list);
9072         slist_free_strings_full(compose->header_list);
9073
9074         slist_free_strings_full(extra_headers);
9075         extra_headers = NULL;
9076
9077         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
9078
9079         g_hash_table_destroy(compose->email_hashtable);
9080
9081         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
9082                         compose->folder_update_callback_id);
9083
9084         procmsg_msginfo_free(&(compose->targetinfo));
9085         procmsg_msginfo_free(&(compose->replyinfo));
9086         procmsg_msginfo_free(&(compose->fwdinfo));
9087
9088         g_free(compose->replyto);
9089         g_free(compose->cc);
9090         g_free(compose->bcc);
9091         g_free(compose->newsgroups);
9092         g_free(compose->followup_to);
9093
9094         g_free(compose->ml_post);
9095
9096         g_free(compose->inreplyto);
9097         g_free(compose->references);
9098         g_free(compose->msgid);
9099         g_free(compose->boundary);
9100
9101         g_free(compose->redirect_filename);
9102         if (compose->undostruct)
9103                 undo_destroy(compose->undostruct);
9104
9105         g_free(compose->sig_str);
9106
9107         g_free(compose->exteditor_file);
9108
9109         g_free(compose->orig_charset);
9110
9111         g_free(compose->privacy_system);
9112         g_free(compose->encdata);
9113
9114 #ifndef USE_ALT_ADDRBOOK
9115         if (addressbook_get_target_compose() == compose)
9116                 addressbook_set_target_compose(NULL);
9117 #endif
9118 #if USE_ENCHANT
9119         if (compose->gtkaspell) {
9120                 gtkaspell_delete(compose->gtkaspell);
9121                 compose->gtkaspell = NULL;
9122         }
9123 #endif
9124
9125         if (!compose->batch) {
9126                 gtk_widget_get_allocation(compose->window, &allocation);
9127                 prefs_common.compose_width = allocation.width;
9128                 prefs_common.compose_height = allocation.height;
9129         }
9130
9131         if (!gtk_widget_get_parent(compose->paned))
9132                 gtk_widget_destroy(compose->paned);
9133         gtk_widget_destroy(compose->popupmenu);
9134
9135         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9136         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
9137         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
9138
9139         gtk_widget_destroy(compose->window);
9140         toolbar_destroy(compose->toolbar);
9141         g_free(compose->toolbar);
9142         cm_mutex_free(compose->mutex);
9143         g_free(compose);
9144 }
9145
9146 static void compose_attach_info_free(AttachInfo *ainfo)
9147 {
9148         g_free(ainfo->file);
9149         g_free(ainfo->content_type);
9150         g_free(ainfo->name);
9151         g_free(ainfo->charset);
9152         g_free(ainfo);
9153 }
9154
9155 static void compose_attach_update_label(Compose *compose)
9156 {
9157         GtkTreeIter iter;
9158         gint i = 1;
9159         gchar *text;
9160         GtkTreeModel *model;
9161         goffset total_size;
9162         AttachInfo *ainfo;
9163
9164         if (compose == NULL)
9165                 return;
9166
9167         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
9168         if (!gtk_tree_model_get_iter_first(model, &iter)) {
9169                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
9170                 return;
9171         }
9172
9173         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
9174         total_size = ainfo->size;
9175         while(gtk_tree_model_iter_next(model, &iter)) {
9176                 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
9177                 total_size += ainfo->size;
9178                 i++;
9179         }
9180         text = g_strdup_printf(" (%d/%s)", i, to_human_readable(total_size));
9181         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
9182         g_free(text);
9183 }
9184
9185 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
9186 {
9187         Compose *compose = (Compose *)data;
9188         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
9189         GtkTreeSelection *selection;
9190         GList *sel, *cur;
9191         GtkTreeModel *model;
9192
9193         selection = gtk_tree_view_get_selection(tree_view);
9194         sel = gtk_tree_selection_get_selected_rows(selection, &model);
9195
9196         if (!sel) 
9197                 return;
9198
9199         for (cur = sel; cur != NULL; cur = cur->next) {
9200                 GtkTreePath *path = cur->data;
9201                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
9202                                                 (model, cur->data);
9203                 cur->data = ref;
9204                 gtk_tree_path_free(path);
9205         }
9206
9207         for (cur = sel; cur != NULL; cur = cur->next) {
9208                 GtkTreeRowReference *ref = cur->data;
9209                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
9210                 GtkTreeIter iter;
9211
9212                 if (gtk_tree_model_get_iter(model, &iter, path))
9213                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
9214                 
9215                 gtk_tree_path_free(path);
9216                 gtk_tree_row_reference_free(ref);
9217         }
9218
9219         g_list_free(sel);
9220         compose_attach_update_label(compose);
9221 }
9222
9223 static struct _AttachProperty
9224 {
9225         GtkWidget *window;
9226         GtkWidget *mimetype_entry;
9227         GtkWidget *encoding_optmenu;
9228         GtkWidget *path_entry;
9229         GtkWidget *filename_entry;
9230         GtkWidget *ok_btn;
9231         GtkWidget *cancel_btn;
9232 } attach_prop;
9233
9234 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
9235 {       
9236         gtk_tree_path_free((GtkTreePath *)ptr);
9237 }
9238
9239 static void compose_attach_property(GtkAction *action, gpointer data)
9240 {
9241         Compose *compose = (Compose *)data;
9242         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
9243         AttachInfo *ainfo;
9244         GtkComboBox *optmenu;
9245         GtkTreeSelection *selection;
9246         GList *sel;
9247         GtkTreeModel *model;
9248         GtkTreeIter iter;
9249         GtkTreePath *path;
9250         static gboolean cancelled;
9251
9252         /* only if one selected */
9253         selection = gtk_tree_view_get_selection(tree_view);
9254         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
9255                 return;
9256
9257         sel = gtk_tree_selection_get_selected_rows(selection, &model);
9258         if (!sel)
9259                 return;
9260
9261         path = (GtkTreePath *) sel->data;
9262         gtk_tree_model_get_iter(model, &iter, path);
9263         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
9264         
9265         if (!ainfo) {
9266                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
9267                 g_list_free(sel);
9268                 return;
9269         }               
9270         g_list_free(sel);
9271
9272         if (!attach_prop.window)
9273                 compose_attach_property_create(&cancelled);
9274         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
9275         gtk_widget_grab_focus(attach_prop.ok_btn);
9276         gtk_widget_show(attach_prop.window);
9277         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
9278                         GTK_WINDOW(compose->window));
9279
9280         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
9281         if (ainfo->encoding == ENC_UNKNOWN)
9282                 combobox_select_by_data(optmenu, ENC_BASE64);
9283         else
9284                 combobox_select_by_data(optmenu, ainfo->encoding);
9285
9286         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
9287                            ainfo->content_type ? ainfo->content_type : "");
9288         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
9289                            ainfo->file ? ainfo->file : "");
9290         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
9291                            ainfo->name ? ainfo->name : "");
9292
9293         for (;;) {
9294                 const gchar *entry_text;
9295                 gchar *text;
9296                 gchar *cnttype = NULL;
9297                 gchar *file = NULL;
9298                 off_t size = 0;
9299
9300                 cancelled = FALSE;
9301                 gtk_main();
9302
9303                 gtk_widget_hide(attach_prop.window);
9304                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
9305                 
9306                 if (cancelled)
9307                         break;
9308
9309                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
9310                 if (*entry_text != '\0') {
9311                         gchar *p;
9312
9313                         text = g_strstrip(g_strdup(entry_text));
9314                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
9315                                 cnttype = g_strdup(text);
9316                                 g_free(text);
9317                         } else {
9318                                 alertpanel_error(_("Invalid MIME type."));
9319                                 g_free(text);
9320                                 continue;
9321                         }
9322                 }
9323
9324                 ainfo->encoding = combobox_get_active_data(optmenu);
9325
9326                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
9327                 if (*entry_text != '\0') {
9328                         if (is_file_exist(entry_text) &&
9329                             (size = get_file_size(entry_text)) > 0)
9330                                 file = g_strdup(entry_text);
9331                         else {
9332                                 alertpanel_error
9333                                         (_("File doesn't exist or is empty."));
9334                                 g_free(cnttype);
9335                                 continue;
9336                         }
9337                 }
9338
9339                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
9340                 if (*entry_text != '\0') {
9341                         g_free(ainfo->name);
9342                         ainfo->name = g_strdup(entry_text);
9343                 }
9344
9345                 if (cnttype) {
9346                         g_free(ainfo->content_type);
9347                         ainfo->content_type = cnttype;
9348                 }
9349                 if (file) {
9350                         g_free(ainfo->file);
9351                         ainfo->file = file;
9352                 }
9353                 if (size)
9354                         ainfo->size = (goffset)size;
9355
9356                 /* update tree store */
9357                 text = to_human_readable(ainfo->size);
9358                 gtk_tree_model_get_iter(model, &iter, path);
9359                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
9360                                    COL_MIMETYPE, ainfo->content_type,
9361                                    COL_SIZE, text,
9362                                    COL_NAME, ainfo->name,
9363                                    COL_CHARSET, ainfo->charset,
9364                                    -1);
9365                 
9366                 break;
9367         }
9368
9369         gtk_tree_path_free(path);
9370 }
9371
9372 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9373 { \
9374         label = gtk_label_new(str); \
9375         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9376                          GTK_FILL, 0, 0, 0); \
9377         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9378  \
9379         entry = gtk_entry_new(); \
9380         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9381                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9382 }
9383
9384 static void compose_attach_property_create(gboolean *cancelled)
9385 {
9386         GtkWidget *window;
9387         GtkWidget *vbox;
9388         GtkWidget *table;
9389         GtkWidget *label;
9390         GtkWidget *mimetype_entry;
9391         GtkWidget *hbox;
9392         GtkWidget *optmenu;
9393         GtkListStore *optmenu_menu;
9394         GtkWidget *path_entry;
9395         GtkWidget *filename_entry;
9396         GtkWidget *hbbox;
9397         GtkWidget *ok_btn;
9398         GtkWidget *cancel_btn;
9399         GList     *mime_type_list, *strlist;
9400         GtkTreeIter iter;
9401
9402         debug_print("Creating attach_property window...\n");
9403
9404         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9405         gtk_widget_set_size_request(window, 480, -1);
9406         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9407         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9408         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9409         g_signal_connect(G_OBJECT(window), "delete_event",
9410                          G_CALLBACK(attach_property_delete_event),
9411                          cancelled);
9412         g_signal_connect(G_OBJECT(window), "key_press_event",
9413                          G_CALLBACK(attach_property_key_pressed),
9414                          cancelled);
9415
9416         vbox = gtk_vbox_new(FALSE, 8);
9417         gtk_container_add(GTK_CONTAINER(window), vbox);
9418
9419         table = gtk_table_new(4, 2, FALSE);
9420         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9421         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9422         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9423
9424         label = gtk_label_new(_("MIME type")); 
9425         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9426                          GTK_FILL, 0, 0, 0); 
9427         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9428         mimetype_entry = gtk_combo_box_text_new_with_entry();
9429         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9430                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9431                          
9432         /* stuff with list */
9433         mime_type_list = procmime_get_mime_type_list();
9434         strlist = NULL;
9435         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9436                 MimeType *type = (MimeType *) mime_type_list->data;
9437                 gchar *tmp;
9438
9439                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9440
9441                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9442                         g_free(tmp);
9443                 else
9444                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9445                                         (GCompareFunc)strcmp2);
9446         }
9447
9448         for (mime_type_list = strlist; mime_type_list != NULL; 
9449                 mime_type_list = mime_type_list->next) {
9450                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9451                 g_free(mime_type_list->data);
9452         }
9453         g_list_free(strlist);
9454         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9455         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9456
9457         label = gtk_label_new(_("Encoding"));
9458         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9459                          GTK_FILL, 0, 0, 0);
9460         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9461
9462         hbox = gtk_hbox_new(FALSE, 0);
9463         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9464                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9465
9466         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9467         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9468
9469         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9470         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9471         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9472         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9473         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9474
9475         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9476
9477         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9478         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9479
9480         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9481                                       &ok_btn, GTK_STOCK_OK,
9482                                       NULL, NULL);
9483         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9484         gtk_widget_grab_default(ok_btn);
9485
9486         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9487                          G_CALLBACK(attach_property_ok),
9488                          cancelled);
9489         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9490                          G_CALLBACK(attach_property_cancel),
9491                          cancelled);
9492
9493         gtk_widget_show_all(vbox);
9494
9495         attach_prop.window           = window;
9496         attach_prop.mimetype_entry   = mimetype_entry;
9497         attach_prop.encoding_optmenu = optmenu;
9498         attach_prop.path_entry       = path_entry;
9499         attach_prop.filename_entry   = filename_entry;
9500         attach_prop.ok_btn           = ok_btn;
9501         attach_prop.cancel_btn       = cancel_btn;
9502 }
9503
9504 #undef SET_LABEL_AND_ENTRY
9505
9506 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9507 {
9508         *cancelled = FALSE;
9509         gtk_main_quit();
9510 }
9511
9512 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9513 {
9514         *cancelled = TRUE;
9515         gtk_main_quit();
9516 }
9517
9518 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9519                                          gboolean *cancelled)
9520 {
9521         *cancelled = TRUE;
9522         gtk_main_quit();
9523
9524         return TRUE;
9525 }
9526
9527 static gboolean attach_property_key_pressed(GtkWidget *widget,
9528                                             GdkEventKey *event,
9529                                             gboolean *cancelled)
9530 {
9531         if (event && event->keyval == GDK_KEY_Escape) {
9532                 *cancelled = TRUE;
9533                 gtk_main_quit();
9534         }
9535         if (event && event->keyval == GDK_KEY_Return) {
9536                 *cancelled = FALSE;
9537                 gtk_main_quit();
9538                 return TRUE;
9539         }
9540         return FALSE;
9541 }
9542
9543 static void compose_exec_ext_editor(Compose *compose)
9544 {
9545 #ifdef G_OS_UNIX
9546         gchar *tmp;
9547         GtkWidget *socket;
9548         GdkNativeWindow socket_wid = 0;
9549         pid_t pid;
9550         gint pipe_fds[2];
9551
9552         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9553                               G_DIR_SEPARATOR, compose);
9554
9555         if (compose_get_ext_editor_uses_socket()) {
9556                 /* Only allow one socket */
9557                 if (compose->exteditor_socket != NULL) {
9558                         if (gtk_widget_is_focus(compose->exteditor_socket)) {
9559                                 /* Move the focus off of the socket */
9560                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9561                         }
9562                         g_free(tmp);
9563                         return;
9564                 }
9565                 /* Create the receiving GtkSocket */
9566                 socket = gtk_socket_new ();
9567                 g_signal_connect (GTK_OBJECT(socket), "plug-removed",
9568                                   G_CALLBACK(compose_ext_editor_plug_removed_cb),
9569                                   compose);
9570                 gtk_box_pack_start(GTK_BOX(compose->edit_vbox), socket, TRUE, TRUE, 0);
9571                 gtk_widget_set_size_request(socket, prefs_common.compose_width, -1);
9572                 /* Realize the socket so that we can use its ID */
9573                 gtk_widget_realize(socket);
9574                 socket_wid = gtk_socket_get_id(GTK_SOCKET (socket));
9575                 compose->exteditor_socket = socket;
9576         }
9577
9578         if (pipe(pipe_fds) < 0) {
9579                 perror("pipe");
9580                 g_free(tmp);
9581                 return;
9582         }
9583
9584         if ((pid = fork()) < 0) {
9585                 perror("fork");
9586                 g_free(tmp);
9587                 return;
9588         }
9589
9590         if (pid != 0) {
9591                 /* close the write side of the pipe */
9592                 close(pipe_fds[1]);
9593
9594                 compose->exteditor_file    = g_strdup(tmp);
9595                 compose->exteditor_pid     = pid;
9596
9597                 compose_set_ext_editor_sensitive(compose, FALSE);
9598
9599 #ifndef G_OS_WIN32
9600                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9601 #else
9602                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9603 #endif
9604                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9605                                                         G_IO_IN,
9606                                                         compose_input_cb,
9607                                                         compose);
9608         } else {        /* process-monitoring process */
9609                 pid_t pid_ed;
9610
9611                 if (setpgid(0, 0))
9612                         perror("setpgid");
9613
9614                 /* close the read side of the pipe */
9615                 close(pipe_fds[0]);
9616
9617                 if (compose_write_body_to_file(compose, tmp) < 0) {
9618                         fd_write_all(pipe_fds[1], "2\n", 2);
9619                         _exit(1);
9620                 }
9621
9622                 pid_ed = compose_exec_ext_editor_real(tmp, socket_wid);
9623                 if (pid_ed < 0) {
9624                         fd_write_all(pipe_fds[1], "1\n", 2);
9625                         _exit(1);
9626                 }
9627
9628                 /* wait until editor is terminated */
9629                 waitpid(pid_ed, NULL, 0);
9630
9631                 fd_write_all(pipe_fds[1], "0\n", 2);
9632
9633                 close(pipe_fds[1]);
9634                 _exit(0);
9635         }
9636
9637         g_free(tmp);
9638 #endif /* G_OS_UNIX */
9639 }
9640
9641 static gboolean compose_can_autosave(Compose *compose)
9642 {
9643         if (compose->privacy_system && compose->use_encryption)
9644                 return prefs_common.autosave && prefs_common.autosave_encrypted;
9645         else
9646                 return prefs_common.autosave;
9647 }
9648
9649 #ifdef G_OS_UNIX
9650 static gboolean compose_get_ext_editor_cmd_valid()
9651 {
9652         gboolean has_s = FALSE;
9653         gboolean has_w = FALSE;
9654         const gchar *p = prefs_common_get_ext_editor_cmd();
9655         if (!p)
9656                 return FALSE;
9657         while ((p = strchr(p, '%'))) {
9658                 p++;
9659                 if (*p == 's') {
9660                         if (has_s)
9661                                 return FALSE;
9662                         has_s = TRUE;
9663                 } else if (*p == 'w') {
9664                         if (has_w)
9665                                 return FALSE;
9666                         has_w = TRUE;
9667                 } else {
9668                         return FALSE;
9669                 }
9670         }
9671         return TRUE;
9672 }
9673
9674 static gint compose_exec_ext_editor_real(const gchar *file, GdkNativeWindow socket_wid)
9675 {
9676         gchar *buf;
9677         gchar *p, *s;
9678         gchar **cmdline;
9679         pid_t pid;
9680
9681         cm_return_val_if_fail(file != NULL, -1);
9682
9683         if ((pid = fork()) < 0) {
9684                 perror("fork");
9685                 return -1;
9686         }
9687
9688         if (pid != 0) return pid;
9689
9690         /* grandchild process */
9691
9692         if (setpgid(0, getppid()))
9693                 perror("setpgid");
9694
9695         if (compose_get_ext_editor_cmd_valid()) {
9696                 if (compose_get_ext_editor_uses_socket()) {
9697                         p = g_strdup(prefs_common_get_ext_editor_cmd());
9698                         s = strstr(p, "%w");
9699                         s[1] = 'u';
9700                         if (strstr(p, "%s") < s)
9701                                 buf = g_strdup_printf(p, file, socket_wid);
9702                         else
9703                                 buf = g_strdup_printf(p, socket_wid, file);
9704                         g_free(p);
9705                 } else {
9706                         buf = g_strdup_printf(prefs_common_get_ext_editor_cmd(), file);
9707                 }
9708         } else {
9709                 if (prefs_common_get_ext_editor_cmd())
9710                         g_warning("External editor command-line is invalid: '%s'",
9711                                   prefs_common_get_ext_editor_cmd());
9712                 buf = g_strdup_printf(DEFAULT_EDITOR_CMD, file);
9713         }
9714
9715         cmdline = strsplit_with_quote(buf, " ", 0);
9716         g_free(buf);
9717         execvp(cmdline[0], cmdline);
9718
9719         perror("execvp");
9720         g_strfreev(cmdline);
9721
9722         _exit(1);
9723 }
9724
9725 static gboolean compose_ext_editor_kill(Compose *compose)
9726 {
9727         pid_t pgid = compose->exteditor_pid * -1;
9728         gint ret;
9729
9730         ret = kill(pgid, 0);
9731
9732         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9733                 AlertValue val;
9734                 gchar *msg;
9735
9736                 msg = g_strdup_printf
9737                         (_("The external editor is still working.\n"
9738                            "Force terminating the process?\n"
9739                            "process group id: %d"), -pgid);
9740                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9741                                       NULL, ALERTFOCUS_FIRST, FALSE, NULL,
9742                                                                                 ALERT_WARNING);
9743                         
9744                 g_free(msg);
9745
9746                 if (val == G_ALERTALTERNATE) {
9747                         g_source_remove(compose->exteditor_tag);
9748                         g_io_channel_shutdown(compose->exteditor_ch,
9749                                               FALSE, NULL);
9750                         g_io_channel_unref(compose->exteditor_ch);
9751
9752                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9753                         waitpid(compose->exteditor_pid, NULL, 0);
9754
9755                         g_warning("Terminated process group id: %d. "
9756                                   "Temporary file: %s", -pgid, compose->exteditor_file);
9757
9758                         compose_set_ext_editor_sensitive(compose, TRUE);
9759
9760                         g_free(compose->exteditor_file);
9761                         compose->exteditor_file    = NULL;
9762                         compose->exteditor_pid     = -1;
9763                         compose->exteditor_ch      = NULL;
9764                         compose->exteditor_tag     = -1;
9765                 } else
9766                         return FALSE;
9767         }
9768
9769         return TRUE;
9770 }
9771
9772 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9773                                  gpointer data)
9774 {
9775         gchar buf[3] = "3";
9776         Compose *compose = (Compose *)data;
9777         gsize bytes_read;
9778
9779         debug_print("Compose: input from monitoring process\n");
9780
9781         if (g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL) != G_IO_STATUS_NORMAL) {
9782                 bytes_read = 0;
9783                 buf[0] = '\0';
9784         }
9785
9786         g_io_channel_shutdown(source, FALSE, NULL);
9787         g_io_channel_unref(source);
9788
9789         waitpid(compose->exteditor_pid, NULL, 0);
9790
9791         if (buf[0] == '0') {            /* success */
9792                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9793                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9794                 GtkTextIter start, end;
9795                 gchar *chars;
9796
9797                 gtk_text_buffer_set_text(buffer, "", -1);
9798                 compose_insert_file(compose, compose->exteditor_file);
9799                 compose_changed_cb(NULL, compose);
9800
9801                 /* Check if we should save the draft or not */
9802                 if (compose_can_autosave(compose))
9803                   compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9804
9805                 if (claws_unlink(compose->exteditor_file) < 0)
9806                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9807
9808                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9809                 gtk_text_buffer_get_start_iter(buffer, &start);
9810                 gtk_text_buffer_get_end_iter(buffer, &end);
9811                 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
9812                 if (chars && strlen(chars) > 0)
9813                         compose->modified = TRUE;
9814                 g_free(chars);
9815         } else if (buf[0] == '1') {     /* failed */
9816                 g_warning("Couldn't exec external editor");
9817                 if (claws_unlink(compose->exteditor_file) < 0)
9818                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9819         } else if (buf[0] == '2') {
9820                 g_warning("Couldn't write to file");
9821         } else if (buf[0] == '3') {
9822                 g_warning("Pipe read failed");
9823         }
9824
9825         compose_set_ext_editor_sensitive(compose, TRUE);
9826
9827         g_free(compose->exteditor_file);
9828         compose->exteditor_file    = NULL;
9829         compose->exteditor_pid     = -1;
9830         compose->exteditor_ch      = NULL;
9831         compose->exteditor_tag     = -1;
9832         if (compose->exteditor_socket) {
9833                 gtk_widget_destroy(compose->exteditor_socket);
9834                 compose->exteditor_socket = NULL;
9835         }
9836
9837
9838         return FALSE;
9839 }
9840
9841 static char *ext_editor_menu_entries[] = {
9842         "Menu/Message/Send",
9843         "Menu/Message/SendLater",
9844         "Menu/Message/InsertFile",
9845         "Menu/Message/InsertSig",
9846         "Menu/Message/ReplaceSig",
9847         "Menu/Message/Save",
9848         "Menu/Message/Print",
9849         "Menu/Edit",
9850 #if USE_ENCHANT
9851         "Menu/Spelling",
9852 #endif
9853         "Menu/Tools/ShowRuler",
9854         "Menu/Tools/Actions",
9855         "Menu/Help",
9856         NULL
9857 };
9858
9859 static void compose_set_ext_editor_sensitive(Compose *compose,
9860                                              gboolean sensitive)
9861 {
9862         int i;
9863
9864         for (i = 0; ext_editor_menu_entries[i]; ++i) {
9865                 cm_menu_set_sensitive_full(compose->ui_manager,
9866                         ext_editor_menu_entries[i], sensitive);
9867         }
9868
9869         if (compose_get_ext_editor_uses_socket()) {
9870                 if (sensitive) {
9871                         if (compose->exteditor_socket)
9872                                 gtk_widget_hide(compose->exteditor_socket);
9873                         gtk_widget_show(compose->scrolledwin);
9874                         if (prefs_common.show_ruler)
9875                                 gtk_widget_show(compose->ruler_hbox);
9876                         /* Fix the focus, as it doesn't go anywhere when the
9877                          * socket is hidden or destroyed */
9878                         gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9879                 } else {
9880                         g_assert (compose->exteditor_socket != NULL);
9881                         /* Fix the focus, as it doesn't go anywhere when the
9882                          * edit box is hidden */
9883                         if (gtk_widget_is_focus(compose->text))
9884                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9885                         gtk_widget_hide(compose->scrolledwin);
9886                         gtk_widget_hide(compose->ruler_hbox);
9887                         gtk_widget_show(compose->exteditor_socket);
9888                 }
9889         } else {
9890                 gtk_widget_set_sensitive(compose->text,                   sensitive);
9891         }
9892         if (compose->toolbar->send_btn)
9893                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9894         if (compose->toolbar->sendl_btn)
9895                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9896         if (compose->toolbar->draft_btn)
9897                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9898         if (compose->toolbar->insert_btn)
9899                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9900         if (compose->toolbar->sig_btn)
9901                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9902         if (compose->toolbar->exteditor_btn)
9903                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9904         if (compose->toolbar->linewrap_current_btn)
9905                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9906         if (compose->toolbar->linewrap_all_btn)
9907                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9908 }
9909
9910 static gboolean compose_get_ext_editor_uses_socket()
9911 {
9912         return (prefs_common_get_ext_editor_cmd() &&
9913                 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9914 }
9915
9916 static gboolean compose_ext_editor_plug_removed_cb(GtkSocket *socket, Compose *compose)
9917 {
9918         compose->exteditor_socket = NULL;
9919         /* returning FALSE allows destruction of the socket */
9920         return FALSE;
9921 }
9922 #endif /* G_OS_UNIX */
9923
9924 /**
9925  * compose_undo_state_changed:
9926  *
9927  * Change the sensivity of the menuentries undo and redo
9928  **/
9929 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9930                                        gint redo_state, gpointer data)
9931 {
9932         Compose *compose = (Compose *)data;
9933
9934         switch (undo_state) {
9935         case UNDO_STATE_TRUE:
9936                 if (!undostruct->undo_state) {
9937                         undostruct->undo_state = TRUE;
9938                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9939                 }
9940                 break;
9941         case UNDO_STATE_FALSE:
9942                 if (undostruct->undo_state) {
9943                         undostruct->undo_state = FALSE;
9944                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9945                 }
9946                 break;
9947         case UNDO_STATE_UNCHANGED:
9948                 break;
9949         case UNDO_STATE_REFRESH:
9950                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9951                 break;
9952         default:
9953                 g_warning("Undo state not recognized");
9954                 break;
9955         }
9956
9957         switch (redo_state) {
9958         case UNDO_STATE_TRUE:
9959                 if (!undostruct->redo_state) {
9960                         undostruct->redo_state = TRUE;
9961                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9962                 }
9963                 break;
9964         case UNDO_STATE_FALSE:
9965                 if (undostruct->redo_state) {
9966                         undostruct->redo_state = FALSE;
9967                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9968                 }
9969                 break;
9970         case UNDO_STATE_UNCHANGED:
9971                 break;
9972         case UNDO_STATE_REFRESH:
9973                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9974                 break;
9975         default:
9976                 g_warning("Redo state not recognized");
9977                 break;
9978         }
9979 }
9980
9981 /* callback functions */
9982
9983 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9984                                         GtkAllocation *allocation,
9985                                         GtkPaned *paned)
9986 {
9987         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9988 }
9989
9990 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9991  * includes "non-client" (windows-izm) in calculation, so this calculation
9992  * may not be accurate.
9993  */
9994 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9995                                         GtkAllocation *allocation,
9996                                         GtkSHRuler *shruler)
9997 {
9998         if (prefs_common.show_ruler) {
9999                 gint char_width = 0, char_height = 0;
10000                 gint line_width_in_chars;
10001
10002                 gtkut_get_font_size(GTK_WIDGET(widget),
10003                                     &char_width, &char_height);
10004                 line_width_in_chars =
10005                         (allocation->width - allocation->x) / char_width;
10006
10007                 /* got the maximum */
10008                 gtk_shruler_set_range(GTK_SHRULER(shruler),
10009                                     0.0, line_width_in_chars, 0);
10010         }
10011
10012         return TRUE;
10013 }
10014
10015 typedef struct {
10016         gchar                   *header;
10017         gchar                   *entry;
10018         ComposePrefType         type;
10019         gboolean                entry_marked;
10020 } HeaderEntryState;
10021
10022 static void account_activated(GtkComboBox *optmenu, gpointer data)
10023 {
10024         Compose *compose = (Compose *)data;
10025
10026         PrefsAccount *ac;
10027         gchar *folderidentifier;
10028         gint account_id = 0;
10029         GtkTreeModel *menu;
10030         GtkTreeIter iter;
10031         GSList *list, *saved_list = NULL;
10032         HeaderEntryState *state;
10033
10034         /* Get ID of active account in the combo box */
10035         menu = gtk_combo_box_get_model(optmenu);
10036         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
10037         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
10038
10039         ac = account_find_from_id(account_id);
10040         cm_return_if_fail(ac != NULL);
10041
10042         if (ac != compose->account) {
10043                 compose_select_account(compose, ac, FALSE);
10044
10045                 for (list = compose->header_list; list; list = list->next) {
10046                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
10047                         
10048                         if (hentry->type == PREF_ACCOUNT || !list->next) {
10049                                 compose_destroy_headerentry(compose, hentry);
10050                                 continue;
10051                         }
10052                         state = g_malloc0(sizeof(HeaderEntryState));
10053                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
10054                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
10055                         state->entry = gtk_editable_get_chars(
10056                                         GTK_EDITABLE(hentry->entry), 0, -1);
10057                         state->type = hentry->type;
10058
10059                         saved_list = g_slist_append(saved_list, state);
10060                         compose_destroy_headerentry(compose, hentry);
10061                 }
10062
10063                 compose->header_last = NULL;
10064                 g_slist_free(compose->header_list);
10065                 compose->header_list = NULL;
10066                 compose->header_nextrow = 1;
10067                 compose_create_header_entry(compose);
10068                 
10069                 if (ac->set_autocc && ac->auto_cc)
10070                         compose_entry_append(compose, ac->auto_cc,
10071                                                 COMPOSE_CC, PREF_ACCOUNT);
10072                 if (ac->set_autobcc && ac->auto_bcc)
10073                         compose_entry_append(compose, ac->auto_bcc,
10074                                                 COMPOSE_BCC, PREF_ACCOUNT);
10075                 if (ac->set_autoreplyto && ac->auto_replyto)
10076                         compose_entry_append(compose, ac->auto_replyto,
10077                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
10078                 
10079                 for (list = saved_list; list; list = list->next) {
10080                         state = (HeaderEntryState *) list->data;
10081
10082                         compose_add_header_entry(compose, state->header,
10083                                                 state->entry, state->type);
10084
10085                         g_free(state->header);
10086                         g_free(state->entry);
10087                         g_free(state);
10088                 }
10089                 g_slist_free(saved_list);
10090
10091                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
10092                                         (ac->protocol == A_NNTP) ? 
10093                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
10094         }
10095
10096         /* Set message save folder */
10097         if (account_get_special_folder(compose->account, F_OUTBOX)) {
10098                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
10099         }
10100         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
10101                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
10102                            
10103         compose_set_save_to(compose, NULL);
10104         if (account_get_special_folder(compose->account, F_OUTBOX)) {
10105                 folderidentifier = folder_item_get_identifier(account_get_special_folder
10106                                   (compose->account, F_OUTBOX));
10107                 compose_set_save_to(compose, folderidentifier);
10108                 g_free(folderidentifier);
10109         }
10110 }
10111
10112 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
10113                             GtkTreeViewColumn *column, Compose *compose)
10114 {
10115         compose_attach_property(NULL, compose);
10116 }
10117
10118 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
10119                                       gpointer data)
10120 {
10121         Compose *compose = (Compose *)data;
10122         GtkTreeSelection *attach_selection;
10123         gint attach_nr_selected;
10124         GtkTreePath *path;
10125         
10126         if (!event) return FALSE;
10127
10128         if (event->button == 3) {
10129                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
10130                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
10131
10132                 /* If no rows, or just one row is selected, right-click should
10133                  * open menu relevant to the row being right-clicked on. We
10134                  * achieve that by selecting the clicked row first. If more
10135                  * than one row is selected, we shouldn't modify the selection,
10136                  * as user may want to remove selected rows (attachments). */
10137                 if (attach_nr_selected < 2) {
10138                         gtk_tree_selection_unselect_all(attach_selection);
10139                         attach_nr_selected = 0;
10140                         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
10141                                         event->x, event->y, &path, NULL, NULL, NULL);
10142                         if (path != NULL) {
10143                                 gtk_tree_selection_select_path(attach_selection, path);
10144                                 gtk_tree_path_free(path);
10145                                 attach_nr_selected++;
10146                         }
10147                 }
10148
10149                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
10150                 /* Properties menu item makes no sense with more than one row
10151                  * selected, the properties dialog can only edit one attachment. */
10152                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected == 1));
10153                         
10154                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
10155                                NULL, NULL, event->button, event->time);
10156                 return TRUE;                           
10157         }
10158
10159         return FALSE;
10160 }
10161
10162 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
10163                                    gpointer data)
10164 {
10165         Compose *compose = (Compose *)data;
10166
10167         if (!event) return FALSE;
10168
10169         switch (event->keyval) {
10170         case GDK_KEY_Delete:
10171                 compose_attach_remove_selected(NULL, compose);
10172                 break;
10173         }
10174         return FALSE;
10175 }
10176
10177 static void compose_allow_user_actions (Compose *compose, gboolean allow)
10178 {
10179         toolbar_comp_set_sensitive(compose, allow);
10180         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
10181         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
10182 #if USE_ENCHANT
10183         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
10184 #endif  
10185         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
10186         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
10187         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
10188         
10189         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
10190
10191 }
10192
10193 static void compose_send_cb(GtkAction *action, gpointer data)
10194 {
10195         Compose *compose = (Compose *)data;
10196
10197 #ifdef G_OS_UNIX
10198         if (compose->exteditor_tag != -1) {
10199                 debug_print("ignoring send: external editor still open\n");
10200                 return;
10201         }
10202 #endif
10203         if (prefs_common.work_offline && 
10204             !inc_offline_should_override(TRUE,
10205                 _("Claws Mail needs network access in order "
10206                   "to send this email.")))
10207                 return;
10208         
10209         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
10210                 g_source_remove(compose->draft_timeout_tag);
10211                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
10212         }
10213
10214         compose_send(compose);
10215 }
10216
10217 static void compose_send_later_cb(GtkAction *action, gpointer data)
10218 {
10219         Compose *compose = (Compose *)data;
10220         ComposeQueueResult val;
10221
10222         inc_lock();
10223         compose_allow_user_actions(compose, FALSE);
10224         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
10225         compose_allow_user_actions(compose, TRUE);
10226         inc_unlock();
10227
10228         if (val == COMPOSE_QUEUE_SUCCESS) {
10229                 compose_close(compose);
10230         } else {
10231                 _display_queue_error(val);
10232         }
10233
10234         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10235 }
10236
10237 #define DRAFTED_AT_EXIT "drafted_at_exit"
10238 static void compose_register_draft(MsgInfo *info)
10239 {
10240         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10241                                       DRAFTED_AT_EXIT, NULL);
10242         FILE *fp = g_fopen(filepath, "ab");
10243         
10244         if (fp) {
10245                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
10246                                 info->msgnum);
10247                 fclose(fp);
10248         }
10249                 
10250         g_free(filepath);       
10251 }
10252
10253 gboolean compose_draft (gpointer data, guint action) 
10254 {
10255         Compose *compose = (Compose *)data;
10256         FolderItem *draft;
10257         gchar *tmp;
10258         gchar *sheaders;
10259         gint msgnum;
10260         MsgFlags flag = {0, 0};
10261         static gboolean lock = FALSE;
10262         MsgInfo *newmsginfo;
10263         FILE *fp;
10264         gboolean target_locked = FALSE;
10265         gboolean err = FALSE;
10266
10267         if (lock) return FALSE;
10268
10269         if (compose->sending)
10270                 return TRUE;
10271
10272         draft = account_get_special_folder(compose->account, F_DRAFT);
10273         cm_return_val_if_fail(draft != NULL, FALSE);
10274         
10275         if (!g_mutex_trylock(compose->mutex)) {
10276                 /* we don't want to lock the mutex once it's available,
10277                  * because as the only other part of compose.c locking
10278                  * it is compose_close - which means once unlocked,
10279                  * the compose struct will be freed */
10280                 debug_print("couldn't lock mutex, probably sending\n");
10281                 return FALSE;
10282         }
10283
10284         lock = TRUE;
10285
10286         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10287                               G_DIR_SEPARATOR, compose);
10288         if ((fp = g_fopen(tmp, "wb")) == NULL) {
10289                 FILE_OP_ERROR(tmp, "fopen");
10290                 goto warn_err;
10291         }
10292
10293         /* chmod for security */
10294         if (change_file_mode_rw(fp, tmp) < 0) {
10295                 FILE_OP_ERROR(tmp, "chmod");
10296                 g_warning("can't change file mode");
10297         }
10298
10299         /* Save draft infos */
10300         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
10301         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
10302
10303         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
10304                 gchar *savefolderid;
10305
10306                 savefolderid = compose_get_save_to(compose);
10307                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
10308                 g_free(savefolderid);
10309         }
10310         if (compose->return_receipt) {
10311                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
10312         }
10313         if (compose->privacy_system) {
10314                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
10315                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
10316                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
10317         }
10318
10319         /* Message-ID of message replying to */
10320         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
10321                 gchar *folderid = NULL;
10322
10323                 if (compose->replyinfo->folder)
10324                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
10325                 if (folderid == NULL)
10326                         folderid = g_strdup("NULL");
10327
10328                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
10329                 g_free(folderid);
10330         }
10331         /* Message-ID of message forwarding to */
10332         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
10333                 gchar *folderid = NULL;
10334
10335                 if (compose->fwdinfo->folder)
10336                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
10337                 if (folderid == NULL)
10338                         folderid = g_strdup("NULL");
10339
10340                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
10341                 g_free(folderid);
10342         }
10343
10344         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
10345         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
10346
10347         sheaders = compose_get_manual_headers_info(compose);
10348         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
10349         g_free(sheaders);
10350
10351         /* end of headers */
10352         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
10353
10354         if (err) {
10355                 fclose(fp);
10356                 goto warn_err;
10357         }
10358
10359         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
10360                 fclose(fp);
10361                 goto warn_err;
10362         }
10363         if (fclose(fp) == EOF) {
10364                 goto warn_err;
10365         }
10366         
10367         flag.perm_flags = MSG_NEW|MSG_UNREAD;
10368         if (compose->targetinfo) {
10369                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
10370                 if (target_locked) 
10371                         flag.perm_flags |= MSG_LOCKED;
10372         }
10373         flag.tmp_flags = MSG_DRAFT;
10374
10375         folder_item_scan(draft);
10376         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
10377                 MsgInfo *tmpinfo = NULL;
10378                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
10379                 if (compose->msgid) {
10380                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
10381                 }
10382                 if (tmpinfo) {
10383                         msgnum = tmpinfo->msgnum;
10384                         procmsg_msginfo_free(&tmpinfo);
10385                         debug_print("got draft msgnum %d from scanning\n", msgnum);
10386                 } else {
10387                         debug_print("didn't get draft msgnum after scanning\n");
10388                 }
10389         } else {
10390                 debug_print("got draft msgnum %d from adding\n", msgnum);
10391         }
10392         if (msgnum < 0) {
10393 warn_err:
10394                 claws_unlink(tmp);
10395                 g_free(tmp);
10396                 if (action != COMPOSE_AUTO_SAVE) {
10397                         if (action != COMPOSE_DRAFT_FOR_EXIT)
10398                                 alertpanel_error(_("Could not save draft."));
10399                         else {
10400                                 AlertValue val;
10401                                 gtkut_window_popup(compose->window);
10402                                 val = alertpanel_full(_("Could not save draft"),
10403                                         _("Could not save draft.\n"
10404                                         "Do you want to cancel exit or discard this email?"),
10405                                           _("_Cancel exit"), _("_Discard email"), NULL, ALERTFOCUS_FIRST,
10406                                           FALSE, NULL, ALERT_QUESTION);
10407                                 if (val == G_ALERTALTERNATE) {
10408                                         lock = FALSE;
10409                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10410                                         compose_close(compose);
10411                                         return TRUE;
10412                                 } else {
10413                                         lock = FALSE;
10414                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10415                                         return FALSE;
10416                                 }
10417                         }
10418                 }
10419                 goto unlock;
10420         }
10421         g_free(tmp);
10422
10423         if (compose->mode == COMPOSE_REEDIT) {
10424                 compose_remove_reedit_target(compose, TRUE);
10425         }
10426
10427         newmsginfo = folder_item_get_msginfo(draft, msgnum);
10428
10429         if (newmsginfo) {
10430                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
10431                 if (target_locked)
10432                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
10433                 else
10434                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
10435                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
10436                         procmsg_msginfo_set_flags(newmsginfo, 0,
10437                                                   MSG_HAS_ATTACHMENT);
10438
10439                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
10440                         compose_register_draft(newmsginfo);
10441                 }
10442                 procmsg_msginfo_free(&newmsginfo);
10443         }
10444         
10445         folder_item_scan(draft);
10446         
10447         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
10448                 lock = FALSE;
10449                 g_mutex_unlock(compose->mutex); /* must be done before closing */
10450                 compose_close(compose);
10451                 return TRUE;
10452         } else {
10453 #ifdef G_OS_WIN32
10454                 GFile *f;
10455                 GFileInfo *fi;
10456                 GTimeVal tv;
10457                 GError *error = NULL;
10458 #else
10459                 GStatBuf s;
10460 #endif
10461                 gchar *path;
10462                 goffset size, mtime;
10463
10464                 path = folder_item_fetch_msg(draft, msgnum);
10465                 if (path == NULL) {
10466                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
10467                         goto unlock;
10468                 }
10469 #ifdef G_OS_WIN32
10470                 f = g_file_new_for_path(path);
10471                 fi = g_file_query_info(f, "standard::size,time::modified",
10472                                 G_FILE_QUERY_INFO_NONE, NULL, &error);
10473                 if (error != NULL) {
10474                         debug_print("couldn't query file info for '%s': %s\n",
10475                                         path, error->message);
10476                         g_error_free(error);
10477                         g_free(path);
10478                         g_object_unref(f);
10479                         goto unlock;
10480                 }
10481                 size = g_file_info_get_size(fi);
10482                 g_file_info_get_modification_time(fi, &tv);
10483                 mtime = tv.tv_sec;
10484                 g_object_unref(fi);
10485                 g_object_unref(f);
10486 #else
10487                 if (g_stat(path, &s) < 0) {
10488                         FILE_OP_ERROR(path, "stat");
10489                         g_free(path);
10490                         goto unlock;
10491                 }
10492                 size = s.st_size;
10493                 mtime = s.st_mtime;
10494 #endif
10495                 g_free(path);
10496
10497                 procmsg_msginfo_free(&(compose->targetinfo));
10498                 compose->targetinfo = procmsg_msginfo_new();
10499                 compose->targetinfo->msgnum = msgnum;
10500                 compose->targetinfo->size = size;
10501                 compose->targetinfo->mtime = mtime;
10502                 compose->targetinfo->folder = draft;
10503                 if (target_locked)
10504                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
10505                 compose->mode = COMPOSE_REEDIT;
10506                 
10507                 if (action == COMPOSE_AUTO_SAVE) {
10508                         compose->autosaved_draft = compose->targetinfo;
10509                 }
10510                 compose->modified = FALSE;
10511                 compose_set_title(compose);
10512         }
10513 unlock:
10514         lock = FALSE;
10515         g_mutex_unlock(compose->mutex);
10516         return TRUE;
10517 }
10518
10519 void compose_clear_exit_drafts(void)
10520 {
10521         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10522                                       DRAFTED_AT_EXIT, NULL);
10523         if (is_file_exist(filepath))
10524                 claws_unlink(filepath);
10525         
10526         g_free(filepath);
10527 }
10528
10529 void compose_reopen_exit_drafts(void)
10530 {
10531         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10532                                       DRAFTED_AT_EXIT, NULL);
10533         FILE *fp = g_fopen(filepath, "rb");
10534         gchar buf[1024];
10535         
10536         if (fp) {
10537                 while (fgets(buf, sizeof(buf), fp)) {
10538                         gchar **parts = g_strsplit(buf, "\t", 2);
10539                         const gchar *folder = parts[0];
10540                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10541                         
10542                         if (folder && *folder && msgnum > -1) {
10543                                 FolderItem *item = folder_find_item_from_identifier(folder);
10544                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10545                                 if (info)
10546                                         compose_reedit(info, FALSE);
10547                         }
10548                         g_strfreev(parts);
10549                 }       
10550                 fclose(fp);
10551         }       
10552         g_free(filepath);
10553         compose_clear_exit_drafts();
10554 }
10555
10556 static void compose_save_cb(GtkAction *action, gpointer data)
10557 {
10558         Compose *compose = (Compose *)data;
10559         compose_draft(compose, COMPOSE_KEEP_EDITING);
10560         compose->rmode = COMPOSE_REEDIT;
10561 }
10562
10563 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10564 {
10565         if (compose && file_list) {
10566                 GList *tmp;
10567
10568                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10569                         gchar *file = (gchar *) tmp->data;
10570                         gchar *utf8_filename = conv_filename_to_utf8(file);
10571                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10572                         compose_changed_cb(NULL, compose);
10573                         if (free_data) {
10574                         g_free(file);
10575                                 tmp->data = NULL;
10576                         }
10577                         g_free(utf8_filename);
10578                 }
10579         }
10580 }
10581
10582 static void compose_attach_cb(GtkAction *action, gpointer data)
10583 {
10584         Compose *compose = (Compose *)data;
10585         GList *file_list;
10586
10587         if (compose->redirect_filename != NULL)
10588                 return;
10589
10590         /* Set focus_window properly, in case we were called via popup menu,
10591          * which unsets it (via focus_out_event callback on compose window). */
10592         manage_window_focus_in(compose->window, NULL, NULL);
10593
10594         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10595
10596         if (file_list) {
10597                 compose_attach_from_list(compose, file_list, TRUE);
10598                 g_list_free(file_list);
10599         }
10600 }
10601
10602 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10603 {
10604         Compose *compose = (Compose *)data;
10605         GList *file_list;
10606         gint files_inserted = 0;
10607
10608         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10609
10610         if (file_list) {
10611                 GList *tmp;
10612
10613                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10614                         gchar *file = (gchar *) tmp->data;
10615                         gchar *filedup = g_strdup(file);
10616                         gchar *shortfile = g_path_get_basename(filedup);
10617                         ComposeInsertResult res;
10618                         /* insert the file if the file is short or if the user confirmed that
10619                            he/she wants to insert the large file */
10620                         res = compose_insert_file(compose, file);
10621                         if (res == COMPOSE_INSERT_READ_ERROR) {
10622                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10623                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10624                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10625                                                         "for the current encoding, insertion may be incorrect."),
10626                                                         shortfile);
10627                         } else if (res == COMPOSE_INSERT_SUCCESS)
10628                                 files_inserted++;
10629
10630                         g_free(shortfile);
10631                         g_free(filedup);
10632                         g_free(file);
10633                 }
10634                 g_list_free(file_list);
10635         }
10636
10637 #ifdef USE_ENCHANT      
10638         if (files_inserted > 0 && compose->gtkaspell && 
10639             compose->gtkaspell->check_while_typing)
10640                 gtkaspell_highlight_all(compose->gtkaspell);
10641 #endif
10642 }
10643
10644 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10645 {
10646         Compose *compose = (Compose *)data;
10647
10648         compose_insert_sig(compose, FALSE);
10649 }
10650
10651 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10652 {
10653         Compose *compose = (Compose *)data;
10654
10655         compose_insert_sig(compose, TRUE);
10656 }
10657
10658 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10659                               gpointer data)
10660 {
10661         gint x, y;
10662         Compose *compose = (Compose *)data;
10663
10664         gtkut_widget_get_uposition(widget, &x, &y);
10665         if (!compose->batch) {
10666                 prefs_common.compose_x = x;
10667                 prefs_common.compose_y = y;
10668         }
10669         if (compose->sending || compose->updating)
10670                 return TRUE;
10671         compose_close_cb(NULL, compose);
10672         return TRUE;
10673 }
10674
10675 void compose_close_toolbar(Compose *compose)
10676 {
10677         compose_close_cb(NULL, compose);
10678 }
10679
10680 static void compose_close_cb(GtkAction *action, gpointer data)
10681 {
10682         Compose *compose = (Compose *)data;
10683         AlertValue val;
10684
10685 #ifdef G_OS_UNIX
10686         if (compose->exteditor_tag != -1) {
10687                 if (!compose_ext_editor_kill(compose))
10688                         return;
10689         }
10690 #endif
10691
10692         if (compose->modified) {
10693                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10694                 if (!g_mutex_trylock(compose->mutex)) {
10695                         /* we don't want to lock the mutex once it's available,
10696                          * because as the only other part of compose.c locking
10697                          * it is compose_close - which means once unlocked,
10698                          * the compose struct will be freed */
10699                         debug_print("couldn't lock mutex, probably sending\n");
10700                         return;
10701                 }
10702                 if (!reedit) {
10703                         val = alertpanel(_("Discard message"),
10704                                  _("This message has been modified. Discard it?"),
10705                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL,
10706                                  ALERTFOCUS_FIRST);
10707                 } else {
10708                         val = alertpanel(_("Save changes"),
10709                                  _("This message has been modified. Save the latest changes?"),
10710                                  _("_Don't save"), _("_Save to Drafts"), GTK_STOCK_CANCEL,
10711                                  ALERTFOCUS_SECOND);
10712                 }
10713                 g_mutex_unlock(compose->mutex);
10714                 switch (val) {
10715                 case G_ALERTDEFAULT:
10716                         if (compose_can_autosave(compose) && !reedit)
10717                                 compose_remove_draft(compose);
10718                         break;
10719                 case G_ALERTALTERNATE:
10720                         compose_draft(data, COMPOSE_QUIT_EDITING);
10721                         return;
10722                 default:
10723                         return;
10724                 }
10725         }
10726
10727         compose_close(compose);
10728 }
10729
10730 static void compose_print_cb(GtkAction *action, gpointer data)
10731 {
10732         Compose *compose = (Compose *) data;
10733
10734         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10735         if (compose->targetinfo)
10736                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10737 }
10738
10739 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10740 {
10741         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10742         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10743         Compose *compose = (Compose *) data;
10744
10745         if (active)
10746                 compose->out_encoding = (CharSet)value;
10747 }
10748
10749 static void compose_address_cb(GtkAction *action, gpointer data)
10750 {
10751         Compose *compose = (Compose *)data;
10752
10753 #ifndef USE_ALT_ADDRBOOK
10754         addressbook_open(compose);
10755 #else
10756         GError* error = NULL;
10757         addressbook_connect_signals(compose);
10758         addressbook_dbus_open(TRUE, &error);
10759         if (error) {
10760                 g_warning("%s", error->message);
10761                 g_error_free(error);
10762         }
10763 #endif
10764 }
10765
10766 static void about_show_cb(GtkAction *action, gpointer data)
10767 {
10768         about_show();
10769 }
10770
10771 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10772 {
10773         Compose *compose = (Compose *)data;
10774         Template *tmpl;
10775         gchar *msg;
10776         AlertValue val;
10777
10778         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10779         cm_return_if_fail(tmpl != NULL);
10780
10781         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10782                               tmpl->name);
10783         val = alertpanel(_("Apply template"), msg,
10784                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL, ALERTFOCUS_FIRST);
10785         g_free(msg);
10786
10787         if (val == G_ALERTDEFAULT)
10788                 compose_template_apply(compose, tmpl, TRUE);
10789         else if (val == G_ALERTALTERNATE)
10790                 compose_template_apply(compose, tmpl, FALSE);
10791 }
10792
10793 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10794 {
10795         Compose *compose = (Compose *)data;
10796
10797 #ifdef G_OS_UNIX
10798         if (compose->exteditor_tag != -1) {
10799                 debug_print("ignoring open external editor: external editor still open\n");
10800                 return;
10801         }
10802 #endif
10803         compose_exec_ext_editor(compose);
10804 }
10805
10806 static void compose_undo_cb(GtkAction *action, gpointer data)
10807 {
10808         Compose *compose = (Compose *)data;
10809         gboolean prev_autowrap = compose->autowrap;
10810
10811         compose->autowrap = FALSE;
10812         undo_undo(compose->undostruct);
10813         compose->autowrap = prev_autowrap;
10814 }
10815
10816 static void compose_redo_cb(GtkAction *action, gpointer data)
10817 {
10818         Compose *compose = (Compose *)data;
10819         gboolean prev_autowrap = compose->autowrap;
10820         
10821         compose->autowrap = FALSE;
10822         undo_redo(compose->undostruct);
10823         compose->autowrap = prev_autowrap;
10824 }
10825
10826 static void entry_cut_clipboard(GtkWidget *entry)
10827 {
10828         if (GTK_IS_EDITABLE(entry))
10829                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10830         else if (GTK_IS_TEXT_VIEW(entry))
10831                 gtk_text_buffer_cut_clipboard(
10832                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10833                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10834                         TRUE);
10835 }
10836
10837 static void entry_copy_clipboard(GtkWidget *entry)
10838 {
10839         if (GTK_IS_EDITABLE(entry))
10840                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10841         else if (GTK_IS_TEXT_VIEW(entry))
10842                 gtk_text_buffer_copy_clipboard(
10843                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10844                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10845 }
10846
10847 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10848                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10849 {
10850         if (GTK_IS_TEXT_VIEW(entry)) {
10851                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10852                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10853                 GtkTextIter start_iter, end_iter;
10854                 gint start, end;
10855                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10856
10857                 if (contents == NULL)
10858                         return;
10859         
10860                 /* we shouldn't delete the selection when middle-click-pasting, or we
10861                  * can't mid-click-paste our own selection */
10862                 if (clip != GDK_SELECTION_PRIMARY) {
10863                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10864                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10865                 }
10866                 
10867                 if (insert_place == NULL) {
10868                         /* if insert_place isn't specified, insert at the cursor.
10869                          * used for Ctrl-V pasting */
10870                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10871                         start = gtk_text_iter_get_offset(&start_iter);
10872                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10873                 } else {
10874                         /* if insert_place is specified, paste here.
10875                          * used for mid-click-pasting */
10876                         start = gtk_text_iter_get_offset(insert_place);
10877                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10878                         if (prefs_common.primary_paste_unselects)
10879                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10880                 }
10881                 
10882                 if (!wrap) {
10883                         /* paste unwrapped: mark the paste so it's not wrapped later */
10884                         end = start + strlen(contents);
10885                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10886                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10887                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10888                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10889                         /* rewrap paragraph now (after a mid-click-paste) */
10890                         mark_start = gtk_text_buffer_get_insert(buffer);
10891                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10892                         gtk_text_iter_backward_char(&start_iter);
10893                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10894                 }
10895         } else if (GTK_IS_EDITABLE(entry))
10896                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10897
10898         compose->modified = TRUE;
10899 }
10900
10901 static void entry_allsel(GtkWidget *entry)
10902 {
10903         if (GTK_IS_EDITABLE(entry))
10904                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10905         else if (GTK_IS_TEXT_VIEW(entry)) {
10906                 GtkTextIter startiter, enditer;
10907                 GtkTextBuffer *textbuf;
10908
10909                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10910                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10911                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10912
10913                 gtk_text_buffer_move_mark_by_name(textbuf, 
10914                         "selection_bound", &startiter);
10915                 gtk_text_buffer_move_mark_by_name(textbuf, 
10916                         "insert", &enditer);
10917         }
10918 }
10919
10920 static void compose_cut_cb(GtkAction *action, gpointer data)
10921 {
10922         Compose *compose = (Compose *)data;
10923         if (compose->focused_editable 
10924 #ifndef GENERIC_UMPC
10925             && gtk_widget_has_focus(compose->focused_editable)
10926 #endif
10927             )
10928                 entry_cut_clipboard(compose->focused_editable);
10929 }
10930
10931 static void compose_copy_cb(GtkAction *action, gpointer data)
10932 {
10933         Compose *compose = (Compose *)data;
10934         if (compose->focused_editable 
10935 #ifndef GENERIC_UMPC
10936             && gtk_widget_has_focus(compose->focused_editable)
10937 #endif
10938             )
10939                 entry_copy_clipboard(compose->focused_editable);
10940 }
10941
10942 static void compose_paste_cb(GtkAction *action, gpointer data)
10943 {
10944         Compose *compose = (Compose *)data;
10945         gint prev_autowrap;
10946         GtkTextBuffer *buffer;
10947         BLOCK_WRAP();
10948         if (compose->focused_editable &&
10949 #ifndef GENERIC_UMPC
10950             gtk_widget_has_focus(compose->focused_editable)
10951 #endif
10952                 )
10953                 entry_paste_clipboard(compose, compose->focused_editable, 
10954                                 prefs_common.linewrap_pastes,
10955                                 GDK_SELECTION_CLIPBOARD, NULL);
10956         UNBLOCK_WRAP();
10957
10958 #ifdef USE_ENCHANT
10959         if (
10960 #ifndef GENERIC_UMPC
10961                 gtk_widget_has_focus(compose->text) &&
10962 #endif
10963             compose->gtkaspell && 
10964             compose->gtkaspell->check_while_typing)
10965                 gtkaspell_highlight_all(compose->gtkaspell);
10966 #endif
10967 }
10968
10969 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10970 {
10971         Compose *compose = (Compose *)data;
10972         gint wrap_quote = prefs_common.linewrap_quote;
10973         if (compose->focused_editable 
10974 #ifndef GENERIC_UMPC
10975             && gtk_widget_has_focus(compose->focused_editable)
10976 #endif
10977             ) {
10978                 /* let text_insert() (called directly or at a later time
10979                  * after the gtk_editable_paste_clipboard) know that 
10980                  * text is to be inserted as a quotation. implemented
10981                  * by using a simple refcount... */
10982                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10983                                                 G_OBJECT(compose->focused_editable),
10984                                                 "paste_as_quotation"));
10985                 g_object_set_data(G_OBJECT(compose->focused_editable),
10986                                     "paste_as_quotation",
10987                                     GINT_TO_POINTER(paste_as_quotation + 1));
10988                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10989                 entry_paste_clipboard(compose, compose->focused_editable, 
10990                                 prefs_common.linewrap_pastes,
10991                                 GDK_SELECTION_CLIPBOARD, NULL);
10992                 prefs_common.linewrap_quote = wrap_quote;
10993         }
10994 }
10995
10996 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10997 {
10998         Compose *compose = (Compose *)data;
10999         gint prev_autowrap;
11000         GtkTextBuffer *buffer;
11001         BLOCK_WRAP();
11002         if (compose->focused_editable 
11003 #ifndef GENERIC_UMPC
11004             && gtk_widget_has_focus(compose->focused_editable)
11005 #endif
11006             )
11007                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
11008                         GDK_SELECTION_CLIPBOARD, NULL);
11009         UNBLOCK_WRAP();
11010
11011 #ifdef USE_ENCHANT
11012         if (
11013 #ifndef GENERIC_UMPC
11014                 gtk_widget_has_focus(compose->text) &&
11015 #endif
11016             compose->gtkaspell && 
11017             compose->gtkaspell->check_while_typing)
11018                 gtkaspell_highlight_all(compose->gtkaspell);
11019 #endif
11020 }
11021
11022 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
11023 {
11024         Compose *compose = (Compose *)data;
11025         gint prev_autowrap;
11026         GtkTextBuffer *buffer;
11027         BLOCK_WRAP();
11028         if (compose->focused_editable 
11029 #ifndef GENERIC_UMPC
11030             && gtk_widget_has_focus(compose->focused_editable)
11031 #endif
11032             )
11033                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
11034                         GDK_SELECTION_CLIPBOARD, NULL);
11035         UNBLOCK_WRAP();
11036
11037 #ifdef USE_ENCHANT
11038         if (
11039 #ifndef GENERIC_UMPC
11040                 gtk_widget_has_focus(compose->text) &&
11041 #endif
11042             compose->gtkaspell &&
11043             compose->gtkaspell->check_while_typing)
11044                 gtkaspell_highlight_all(compose->gtkaspell);
11045 #endif
11046 }
11047
11048 static void compose_allsel_cb(GtkAction *action, gpointer data)
11049 {
11050         Compose *compose = (Compose *)data;
11051         if (compose->focused_editable 
11052 #ifndef GENERIC_UMPC
11053             && gtk_widget_has_focus(compose->focused_editable)
11054 #endif
11055             )
11056                 entry_allsel(compose->focused_editable);
11057 }
11058
11059 static void textview_move_beginning_of_line (GtkTextView *text)
11060 {
11061         GtkTextBuffer *buffer;
11062         GtkTextMark *mark;
11063         GtkTextIter ins;
11064
11065         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11066
11067         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11068         mark = gtk_text_buffer_get_insert(buffer);
11069         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11070         gtk_text_iter_set_line_offset(&ins, 0);
11071         gtk_text_buffer_place_cursor(buffer, &ins);
11072 }
11073
11074 static void textview_move_forward_character (GtkTextView *text)
11075 {
11076         GtkTextBuffer *buffer;
11077         GtkTextMark *mark;
11078         GtkTextIter ins;
11079
11080         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11081
11082         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11083         mark = gtk_text_buffer_get_insert(buffer);
11084         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11085         if (gtk_text_iter_forward_cursor_position(&ins))
11086                 gtk_text_buffer_place_cursor(buffer, &ins);
11087 }
11088
11089 static void textview_move_backward_character (GtkTextView *text)
11090 {
11091         GtkTextBuffer *buffer;
11092         GtkTextMark *mark;
11093         GtkTextIter ins;
11094
11095         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11096
11097         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11098         mark = gtk_text_buffer_get_insert(buffer);
11099         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11100         if (gtk_text_iter_backward_cursor_position(&ins))
11101                 gtk_text_buffer_place_cursor(buffer, &ins);
11102 }
11103
11104 static void textview_move_forward_word (GtkTextView *text)
11105 {
11106         GtkTextBuffer *buffer;
11107         GtkTextMark *mark;
11108         GtkTextIter ins;
11109         gint count;
11110
11111         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11112
11113         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11114         mark = gtk_text_buffer_get_insert(buffer);
11115         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11116         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
11117         if (gtk_text_iter_forward_word_ends(&ins, count)) {
11118                 gtk_text_iter_backward_word_start(&ins);
11119                 gtk_text_buffer_place_cursor(buffer, &ins);
11120         }
11121 }
11122
11123 static void textview_move_backward_word (GtkTextView *text)
11124 {
11125         GtkTextBuffer *buffer;
11126         GtkTextMark *mark;
11127         GtkTextIter ins;
11128
11129         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11130
11131         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11132         mark = gtk_text_buffer_get_insert(buffer);
11133         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11134         if (gtk_text_iter_backward_word_starts(&ins, 1))
11135                 gtk_text_buffer_place_cursor(buffer, &ins);
11136 }
11137
11138 static void textview_move_end_of_line (GtkTextView *text)
11139 {
11140         GtkTextBuffer *buffer;
11141         GtkTextMark *mark;
11142         GtkTextIter ins;
11143
11144         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11145
11146         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11147         mark = gtk_text_buffer_get_insert(buffer);
11148         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11149         if (gtk_text_iter_forward_to_line_end(&ins))
11150                 gtk_text_buffer_place_cursor(buffer, &ins);
11151 }
11152
11153 static void textview_move_next_line (GtkTextView *text)
11154 {
11155         GtkTextBuffer *buffer;
11156         GtkTextMark *mark;
11157         GtkTextIter ins;
11158         gint offset;
11159
11160         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11161
11162         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11163         mark = gtk_text_buffer_get_insert(buffer);
11164         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11165         offset = gtk_text_iter_get_line_offset(&ins);
11166         if (gtk_text_iter_forward_line(&ins)) {
11167                 gtk_text_iter_set_line_offset(&ins, offset);
11168                 gtk_text_buffer_place_cursor(buffer, &ins);
11169         }
11170 }
11171
11172 static void textview_move_previous_line (GtkTextView *text)
11173 {
11174         GtkTextBuffer *buffer;
11175         GtkTextMark *mark;
11176         GtkTextIter ins;
11177         gint offset;
11178
11179         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11180
11181         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11182         mark = gtk_text_buffer_get_insert(buffer);
11183         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11184         offset = gtk_text_iter_get_line_offset(&ins);
11185         if (gtk_text_iter_backward_line(&ins)) {
11186                 gtk_text_iter_set_line_offset(&ins, offset);
11187                 gtk_text_buffer_place_cursor(buffer, &ins);
11188         }
11189 }
11190
11191 static void textview_delete_forward_character (GtkTextView *text)
11192 {
11193         GtkTextBuffer *buffer;
11194         GtkTextMark *mark;
11195         GtkTextIter ins, end_iter;
11196
11197         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11198
11199         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11200         mark = gtk_text_buffer_get_insert(buffer);
11201         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11202         end_iter = ins;
11203         if (gtk_text_iter_forward_char(&end_iter)) {
11204                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
11205         }
11206 }
11207
11208 static void textview_delete_backward_character (GtkTextView *text)
11209 {
11210         GtkTextBuffer *buffer;
11211         GtkTextMark *mark;
11212         GtkTextIter ins, end_iter;
11213
11214         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11215
11216         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11217         mark = gtk_text_buffer_get_insert(buffer);
11218         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11219         end_iter = ins;
11220         if (gtk_text_iter_backward_char(&end_iter)) {
11221                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
11222         }
11223 }
11224
11225 static void textview_delete_forward_word (GtkTextView *text)
11226 {
11227         GtkTextBuffer *buffer;
11228         GtkTextMark *mark;
11229         GtkTextIter ins, end_iter;
11230
11231         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11232
11233         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11234         mark = gtk_text_buffer_get_insert(buffer);
11235         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11236         end_iter = ins;
11237         if (gtk_text_iter_forward_word_end(&end_iter)) {
11238                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
11239         }
11240 }
11241
11242 static void textview_delete_backward_word (GtkTextView *text)
11243 {
11244         GtkTextBuffer *buffer;
11245         GtkTextMark *mark;
11246         GtkTextIter ins, end_iter;
11247
11248         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11249
11250         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11251         mark = gtk_text_buffer_get_insert(buffer);
11252         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11253         end_iter = ins;
11254         if (gtk_text_iter_backward_word_start(&end_iter)) {
11255                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
11256         }
11257 }
11258
11259 static void textview_delete_line (GtkTextView *text)
11260 {
11261         GtkTextBuffer *buffer;
11262         GtkTextMark *mark;
11263         GtkTextIter ins, start_iter, end_iter;
11264
11265         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11266
11267         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11268         mark = gtk_text_buffer_get_insert(buffer);
11269         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11270
11271         start_iter = ins;
11272         gtk_text_iter_set_line_offset(&start_iter, 0);
11273
11274         end_iter = ins;
11275         if (gtk_text_iter_ends_line(&end_iter)){
11276                 if (!gtk_text_iter_forward_char(&end_iter))
11277                         gtk_text_iter_backward_char(&start_iter);
11278         }
11279         else 
11280                 gtk_text_iter_forward_to_line_end(&end_iter);
11281         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
11282 }
11283
11284 static void textview_delete_to_line_end (GtkTextView *text)
11285 {
11286         GtkTextBuffer *buffer;
11287         GtkTextMark *mark;
11288         GtkTextIter ins, end_iter;
11289
11290         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11291
11292         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11293         mark = gtk_text_buffer_get_insert(buffer);
11294         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11295         end_iter = ins;
11296         if (gtk_text_iter_ends_line(&end_iter))
11297                 gtk_text_iter_forward_char(&end_iter);
11298         else
11299                 gtk_text_iter_forward_to_line_end(&end_iter);
11300         gtk_text_buffer_delete(buffer, &ins, &end_iter);
11301 }
11302
11303 #define DO_ACTION(name, act) {                                          \
11304         if(!strcmp(name, a_name)) {                                     \
11305                 return act;                                             \
11306         }                                                               \
11307 }
11308 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
11309 {
11310         const gchar *a_name = gtk_action_get_name(action);
11311         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
11312         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
11313         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
11314         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
11315         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
11316         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
11317         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
11318         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
11319         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
11320         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
11321         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
11322         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
11323         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
11324         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
11325         return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED;
11326 }
11327
11328 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
11329 {
11330         Compose *compose = (Compose *)data;
11331         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11332         ComposeCallAdvancedAction action = COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED;
11333         
11334         action = compose_call_advanced_action_from_path(gaction);
11335
11336         static struct {
11337                 void (*do_action) (GtkTextView *text);
11338         } action_table[] = {
11339                 {textview_move_beginning_of_line},
11340                 {textview_move_forward_character},
11341                 {textview_move_backward_character},
11342                 {textview_move_forward_word},
11343                 {textview_move_backward_word},
11344                 {textview_move_end_of_line},
11345                 {textview_move_next_line},
11346                 {textview_move_previous_line},
11347                 {textview_delete_forward_character},
11348                 {textview_delete_backward_character},
11349                 {textview_delete_forward_word},
11350                 {textview_delete_backward_word},
11351                 {textview_delete_line},
11352                 {textview_delete_to_line_end}
11353         };
11354
11355         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
11356
11357         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
11358             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
11359                 if (action_table[action].do_action)
11360                         action_table[action].do_action(text);
11361                 else
11362                         g_warning("Not implemented yet.");
11363         }
11364 }
11365
11366 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
11367 {
11368         GtkAllocation allocation;
11369         GtkWidget *parent;
11370         gchar *str = NULL;
11371         
11372         if (GTK_IS_EDITABLE(widget)) {
11373                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
11374                 gtk_editable_set_position(GTK_EDITABLE(widget), 
11375                         strlen(str));
11376                 g_free(str);
11377                 if ((parent = gtk_widget_get_parent(widget))
11378                  && (parent = gtk_widget_get_parent(parent))
11379                  && (parent = gtk_widget_get_parent(parent))) {
11380                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
11381                                 gtk_widget_get_allocation(widget, &allocation);
11382                                 gint y = allocation.y;
11383                                 gint height = allocation.height;
11384                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
11385                                         (GTK_SCROLLED_WINDOW(parent));
11386
11387                                 gfloat value = gtk_adjustment_get_value(shown);
11388                                 gfloat upper = gtk_adjustment_get_upper(shown);
11389                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
11390                                 if (y < (int)value) {
11391                                         gtk_adjustment_set_value(shown, y - 1);
11392                                 }
11393                                 if ((y + height) > ((int)value + (int)page_size)) {
11394                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
11395                                                 gtk_adjustment_set_value(shown, 
11396                                                         y + height - (int)page_size - 1);
11397                                         } else {
11398                                                 gtk_adjustment_set_value(shown, 
11399                                                         (int)upper - (int)page_size - 1);
11400                                         }
11401                                 }
11402                         }
11403                 }
11404         }
11405
11406         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
11407                 compose->focused_editable = widget;
11408         
11409 #ifdef GENERIC_UMPC
11410         if (GTK_IS_TEXT_VIEW(widget) 
11411             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
11412                 g_object_ref(compose->notebook);
11413                 g_object_ref(compose->edit_vbox);
11414                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11415                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11416                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
11417                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
11418                 g_object_unref(compose->notebook);
11419                 g_object_unref(compose->edit_vbox);
11420                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11421                                         G_CALLBACK(compose_grab_focus_cb),
11422                                         compose);
11423                 gtk_widget_grab_focus(widget);
11424                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11425                                         G_CALLBACK(compose_grab_focus_cb),
11426                                         compose);
11427         } else if (!GTK_IS_TEXT_VIEW(widget) 
11428                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
11429                 g_object_ref(compose->notebook);
11430                 g_object_ref(compose->edit_vbox);
11431                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11432                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11433                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
11434                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
11435                 g_object_unref(compose->notebook);
11436                 g_object_unref(compose->edit_vbox);
11437                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11438                                         G_CALLBACK(compose_grab_focus_cb),
11439                                         compose);
11440                 gtk_widget_grab_focus(widget);
11441                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11442                                         G_CALLBACK(compose_grab_focus_cb),
11443                                         compose);
11444         }
11445 #endif
11446 }
11447
11448 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
11449 {
11450         compose->modified = TRUE;
11451 /*      compose_beautify_paragraph(compose, NULL, TRUE); */
11452 #ifndef GENERIC_UMPC
11453         compose_set_title(compose);
11454 #endif
11455 }
11456
11457 static void compose_wrap_cb(GtkAction *action, gpointer data)
11458 {
11459         Compose *compose = (Compose *)data;
11460         compose_beautify_paragraph(compose, NULL, TRUE);
11461 }
11462
11463 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
11464 {
11465         Compose *compose = (Compose *)data;
11466         compose_wrap_all_full(compose, TRUE);
11467 }
11468
11469 static void compose_find_cb(GtkAction *action, gpointer data)
11470 {
11471         Compose *compose = (Compose *)data;
11472
11473         message_search_compose(compose);
11474 }
11475
11476 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
11477                                          gpointer        data)
11478 {
11479         Compose *compose = (Compose *)data;
11480         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11481         if (compose->autowrap)
11482                 compose_wrap_all_full(compose, TRUE);
11483         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11484 }
11485
11486 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
11487                                          gpointer        data)
11488 {
11489         Compose *compose = (Compose *)data;
11490         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11491 }
11492
11493 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
11494 {
11495         Compose *compose = (Compose *)data;
11496
11497         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11498         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_sign_btn), compose->use_signing);
11499 }
11500
11501 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
11502 {
11503         Compose *compose = (Compose *)data;
11504
11505         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11506         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(compose->toolbar->privacy_encrypt_btn), compose->use_encryption);
11507 }
11508
11509 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
11510 {
11511         g_free(compose->privacy_system);
11512         g_free(compose->encdata);
11513
11514         compose->privacy_system = g_strdup(account->default_privacy_system);
11515         compose_update_privacy_system_menu_item(compose, warn);
11516 }
11517
11518 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
11519 {
11520         Compose *compose = (Compose *)data;
11521
11522         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
11523                 gtk_widget_show(compose->ruler_hbox);
11524                 prefs_common.show_ruler = TRUE;
11525         } else {
11526                 gtk_widget_hide(compose->ruler_hbox);
11527                 gtk_widget_queue_resize(compose->edit_vbox);
11528                 prefs_common.show_ruler = FALSE;
11529         }
11530 }
11531
11532 static void compose_attach_drag_received_cb (GtkWidget          *widget,
11533                                              GdkDragContext     *context,
11534                                              gint                x,
11535                                              gint                y,
11536                                              GtkSelectionData   *data,
11537                                              guint               info,
11538                                              guint               time,
11539                                              gpointer            user_data)
11540 {
11541         Compose *compose = (Compose *)user_data;
11542         GList *list, *tmp;
11543         GdkAtom type;
11544
11545         type = gtk_selection_data_get_data_type(data);
11546         if ((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11547            && gtk_drag_get_source_widget(context) !=
11548                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11549                 list = uri_list_extract_filenames(
11550                         (const gchar *)gtk_selection_data_get_data(data));
11551                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11552                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11553                         compose_attach_append
11554                                 (compose, (const gchar *)tmp->data,
11555                                  utf8_filename, NULL, NULL);
11556                         g_free(utf8_filename);
11557                 }
11558                 if (list) compose_changed_cb(NULL, compose);
11559                 list_free_strings(list);
11560                 g_list_free(list);
11561         } else if (gtk_drag_get_source_widget(context) 
11562                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11563                 /* comes from our summaryview */
11564                 SummaryView * summaryview = NULL;
11565                 GSList * list = NULL, *cur = NULL;
11566                 
11567                 if (mainwindow_get_mainwindow())
11568                         summaryview = mainwindow_get_mainwindow()->summaryview;
11569                 
11570                 if (summaryview)
11571                         list = summary_get_selected_msg_list(summaryview);
11572                 
11573                 for (cur = list; cur; cur = cur->next) {
11574                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11575                         gchar *file = NULL;
11576                         if (msginfo)
11577                                 file = procmsg_get_message_file_full(msginfo, 
11578                                         TRUE, TRUE);
11579                         if (file) {
11580                                 compose_attach_append(compose, (const gchar *)file, 
11581                                         (const gchar *)file, "message/rfc822", NULL);
11582                                 g_free(file);
11583                         }
11584                 }
11585                 g_slist_free(list);
11586         }
11587 }
11588
11589 static gboolean compose_drag_drop(GtkWidget *widget,
11590                                   GdkDragContext *drag_context,
11591                                   gint x, gint y,
11592                                   guint time, gpointer user_data)
11593 {
11594         /* not handling this signal makes compose_insert_drag_received_cb
11595          * called twice */
11596         return TRUE;                                     
11597 }
11598
11599 static gboolean completion_set_focus_to_subject
11600                                         (GtkWidget    *widget,
11601                                          GdkEventKey  *event,
11602                                          Compose      *compose)
11603 {
11604         cm_return_val_if_fail(compose != NULL, FALSE);
11605
11606         /* make backtab move to subject field */
11607         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11608                 gtk_widget_grab_focus(compose->subject_entry);
11609                 return TRUE;
11610         }
11611         return FALSE;
11612 }
11613
11614 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11615                                              GdkDragContext     *drag_context,
11616                                              gint                x,
11617                                              gint                y,
11618                                              GtkSelectionData   *data,
11619                                              guint               info,
11620                                              guint               time,
11621                                              gpointer            user_data)
11622 {
11623         Compose *compose = (Compose *)user_data;
11624         GList *list, *tmp;
11625         GdkAtom type;
11626         guint num_files;
11627         gchar *msg;
11628
11629         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11630          * does not work */
11631         type = gtk_selection_data_get_data_type(data);
11632         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11633                 AlertValue val = G_ALERTDEFAULT;
11634                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11635
11636                 list = uri_list_extract_filenames(ddata);
11637                 num_files = g_list_length(list);
11638                 if (list == NULL && strstr(ddata, "://")) {
11639                         /* Assume a list of no files, and data has ://, is a remote link */
11640                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11641                         gchar *tmpfile = get_tmp_file();
11642                         str_write_to_file(tmpdata, tmpfile);
11643                         g_free(tmpdata);  
11644                         compose_insert_file(compose, tmpfile);
11645                         claws_unlink(tmpfile);
11646                         g_free(tmpfile);
11647                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11648                         compose_beautify_paragraph(compose, NULL, TRUE);
11649                         return;
11650                 }
11651                 switch (prefs_common.compose_dnd_mode) {
11652                         case COMPOSE_DND_ASK:
11653                                 msg = g_strdup_printf(
11654                                                 ngettext(
11655                                                         "Do you want to insert the contents of the file "
11656                                                         "into the message body, or attach it to the email?",
11657                                                         "Do you want to insert the contents of the %d files "
11658                                                         "into the message body, or attach them to the email?",
11659                                                         num_files),
11660                                                 num_files);
11661                                 val = alertpanel_full(_("Insert or attach?"), msg,
11662                                           GTK_STOCK_CANCEL, _("_Insert"), _("_Attach"),
11663                                                 ALERTFOCUS_SECOND,
11664                                           TRUE, NULL, ALERT_QUESTION);
11665                                 g_free(msg);
11666                                 break;
11667                         case COMPOSE_DND_INSERT:
11668                                 val = G_ALERTALTERNATE;
11669                                 break;
11670                         case COMPOSE_DND_ATTACH:
11671                                 val = G_ALERTOTHER;
11672                                 break;
11673                         default:
11674                                 /* unexpected case */
11675                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11676                 }
11677
11678                 if (val & G_ALERTDISABLE) {
11679                         val &= ~G_ALERTDISABLE;
11680                         /* remember what action to perform by default, only if we don't click Cancel */
11681                         if (val == G_ALERTALTERNATE)
11682                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11683                         else if (val == G_ALERTOTHER)
11684                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11685                 }
11686
11687                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11688                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11689                         list_free_strings(list);
11690                         g_list_free(list);
11691                         return;
11692                 } else if (val == G_ALERTOTHER) {
11693                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11694                         list_free_strings(list);
11695                         g_list_free(list);
11696                         return;
11697                 } 
11698
11699                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11700                         compose_insert_file(compose, (const gchar *)tmp->data);
11701                 }
11702                 list_free_strings(list);
11703                 g_list_free(list);
11704                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11705                 return;
11706         }
11707 }
11708
11709 static void compose_header_drag_received_cb (GtkWidget          *widget,
11710                                              GdkDragContext     *drag_context,
11711                                              gint                x,
11712                                              gint                y,
11713                                              GtkSelectionData   *data,
11714                                              guint               info,
11715                                              guint               time,
11716                                              gpointer            user_data)
11717 {
11718         GtkEditable *entry = (GtkEditable *)user_data;
11719         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11720
11721         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11722          * does not work */
11723
11724         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11725                 gchar *decoded=g_new(gchar, strlen(email));
11726                 int start = 0;
11727
11728                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11729                 gtk_editable_delete_text(entry, 0, -1);
11730                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11731                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11732                 g_free(decoded);
11733                 return;
11734         }
11735         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11736 }
11737
11738 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11739 {
11740         Compose *compose = (Compose *)data;
11741
11742         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11743                 compose->return_receipt = TRUE;
11744         else
11745                 compose->return_receipt = FALSE;
11746 }
11747
11748 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11749 {
11750         Compose *compose = (Compose *)data;
11751
11752         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11753                 compose->remove_references = TRUE;
11754         else
11755                 compose->remove_references = FALSE;
11756 }
11757
11758 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11759                                         ComposeHeaderEntry *headerentry)
11760 {
11761         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11762         gtk_widget_modify_base(GTK_WIDGET(headerentry->entry), GTK_STATE_NORMAL, NULL);
11763         gtk_widget_modify_text(GTK_WIDGET(headerentry->entry), GTK_STATE_NORMAL, NULL);
11764         return FALSE;
11765 }
11766
11767 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11768                                             GdkEventKey *event,
11769                                             ComposeHeaderEntry *headerentry)
11770 {
11771         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11772             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11773             !(event->state & GDK_MODIFIER_MASK) &&
11774             (event->keyval == GDK_KEY_BackSpace) &&
11775             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11776                 gtk_container_remove
11777                         (GTK_CONTAINER(headerentry->compose->header_table),
11778                          headerentry->combo);
11779                 gtk_container_remove
11780                         (GTK_CONTAINER(headerentry->compose->header_table),
11781                          headerentry->entry);
11782                 headerentry->compose->header_list =
11783                         g_slist_remove(headerentry->compose->header_list,
11784                                        headerentry);
11785                 g_free(headerentry);
11786         } else  if (event->keyval == GDK_KEY_Tab) {
11787                 if (headerentry->compose->header_last == headerentry) {
11788                         /* Override default next focus, and give it to subject_entry
11789                          * instead of notebook tabs
11790                          */
11791                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11792                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11793                         return TRUE;
11794                 }
11795         }
11796         return FALSE;
11797 }
11798
11799 static gboolean scroll_postpone(gpointer data)
11800 {
11801         Compose *compose = (Compose *)data;
11802
11803         if (compose->batch)
11804                 return FALSE;
11805
11806         GTK_EVENTS_FLUSH();
11807         compose_show_first_last_header(compose, FALSE);
11808         return FALSE;
11809 }
11810
11811 static void compose_headerentry_changed_cb(GtkWidget *entry,
11812                                     ComposeHeaderEntry *headerentry)
11813 {
11814         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11815                 compose_create_header_entry(headerentry->compose);
11816                 g_signal_handlers_disconnect_matched
11817                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11818                          0, 0, NULL, NULL, headerentry);
11819
11820                 if (!headerentry->compose->batch)
11821                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11822         }
11823 }
11824
11825 static gboolean compose_defer_auto_save_draft(Compose *compose)
11826 {
11827         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11828         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11829         return FALSE;
11830 }
11831
11832 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11833 {
11834         GtkAdjustment *vadj;
11835
11836         cm_return_if_fail(compose);
11837
11838         if(compose->batch)
11839                 return;
11840
11841         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11842         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11843         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11844                                 gtk_widget_get_parent(compose->header_table)));
11845         gtk_adjustment_set_value(vadj, (show_first ?
11846                                 gtk_adjustment_get_lower(vadj) :
11847                                 (gtk_adjustment_get_upper(vadj) -
11848                                 gtk_adjustment_get_page_size(vadj))));
11849         gtk_adjustment_changed(vadj);
11850 }
11851
11852 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11853                           const gchar *text, gint len, Compose *compose)
11854 {
11855         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11856                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11857         GtkTextMark *mark;
11858
11859         cm_return_if_fail(text != NULL);
11860
11861         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11862                                         G_CALLBACK(text_inserted),
11863                                         compose);
11864         if (paste_as_quotation) {
11865                 gchar *new_text;
11866                 const gchar *qmark;
11867                 guint pos = 0;
11868                 GtkTextIter start_iter;
11869
11870                 if (len < 0)
11871                         len = strlen(text);
11872
11873                 new_text = g_strndup(text, len);
11874
11875                 qmark = compose_quote_char_from_context(compose);
11876
11877                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11878                 gtk_text_buffer_place_cursor(buffer, iter);
11879
11880                 pos = gtk_text_iter_get_offset(iter);
11881
11882                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11883                                                   _("Quote format error at line %d."));
11884                 quote_fmt_reset_vartable();
11885                 g_free(new_text);
11886                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11887                                   GINT_TO_POINTER(paste_as_quotation - 1));
11888                                   
11889                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11890                 gtk_text_buffer_place_cursor(buffer, iter);
11891                 gtk_text_buffer_delete_mark(buffer, mark);
11892
11893                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11894                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11895                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11896                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11897                 gtk_text_buffer_delete_mark(buffer, mark);
11898         } else {
11899                 if (strcmp(text, "\n") || compose->automatic_break
11900                 || gtk_text_iter_starts_line(iter)) {
11901                         GtkTextIter before_ins;
11902                         gtk_text_buffer_insert(buffer, iter, text, len);
11903                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11904                                 before_ins = *iter; 
11905                                 gtk_text_iter_backward_chars(&before_ins, len);
11906                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11907                         }
11908                 } else {
11909                         /* check if the preceding is just whitespace or quote */
11910                         GtkTextIter start_line;
11911                         gchar *tmp = NULL, *quote = NULL;
11912                         gint quote_len = 0, is_normal = 0;
11913                         start_line = *iter;
11914                         gtk_text_iter_set_line_offset(&start_line, 0); 
11915                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11916                         g_strstrip(tmp);
11917
11918                         if (*tmp == '\0') {
11919                                 is_normal = 1;
11920                         } else {
11921                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11922                                 if (quote)
11923                                         is_normal = 1;
11924                                 g_free(quote);
11925                         }
11926                         g_free(tmp);
11927                         
11928                         if (is_normal) {
11929                                 gtk_text_buffer_insert(buffer, iter, text, len);
11930                         } else {
11931                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11932                                         iter, text, len, "no_join", NULL);
11933                         }
11934                 }
11935         }
11936         
11937         if (!paste_as_quotation) {
11938                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11939                 compose_beautify_paragraph(compose, iter, FALSE);
11940                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11941                 gtk_text_buffer_delete_mark(buffer, mark);
11942         }
11943
11944         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11945                                           G_CALLBACK(text_inserted),
11946                                           compose);
11947         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11948
11949         if (compose_can_autosave(compose) && 
11950             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11951             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11952                 compose->draft_timeout_tag = g_timeout_add
11953                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11954 }
11955
11956 #if USE_ENCHANT
11957 static void compose_check_all(GtkAction *action, gpointer data)
11958 {
11959         Compose *compose = (Compose *)data;
11960         if (!compose->gtkaspell)
11961                 return;
11962                 
11963         if (gtk_widget_has_focus(compose->subject_entry))
11964                 claws_spell_entry_check_all(
11965                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11966         else
11967                 gtkaspell_check_all(compose->gtkaspell);
11968 }
11969
11970 static void compose_highlight_all(GtkAction *action, gpointer data)
11971 {
11972         Compose *compose = (Compose *)data;
11973         if (compose->gtkaspell) {
11974                 claws_spell_entry_recheck_all(
11975                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11976                 gtkaspell_highlight_all(compose->gtkaspell);
11977         }
11978 }
11979
11980 static void compose_check_backwards(GtkAction *action, gpointer data)
11981 {
11982         Compose *compose = (Compose *)data;
11983         if (!compose->gtkaspell) {
11984                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11985                 return;
11986         }
11987
11988         if (gtk_widget_has_focus(compose->subject_entry))
11989                 claws_spell_entry_check_backwards(
11990                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11991         else
11992                 gtkaspell_check_backwards(compose->gtkaspell);
11993 }
11994
11995 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11996 {
11997         Compose *compose = (Compose *)data;
11998         if (!compose->gtkaspell) {
11999                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
12000                 return;
12001         }
12002
12003         if (gtk_widget_has_focus(compose->subject_entry))
12004                 claws_spell_entry_check_forwards_go(
12005                         CLAWS_SPELL_ENTRY(compose->subject_entry));
12006         else
12007                 gtkaspell_check_forwards_go(compose->gtkaspell);
12008 }
12009 #endif
12010
12011 /*!
12012  *\brief        Guess originating forward account from MsgInfo and several 
12013  *              "common preference" settings. Return NULL if no guess. 
12014  */
12015 static PrefsAccount *compose_find_account(MsgInfo *msginfo)
12016 {
12017         PrefsAccount *account = NULL;
12018         
12019         cm_return_val_if_fail(msginfo, NULL);
12020         cm_return_val_if_fail(msginfo->folder, NULL);
12021         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
12022
12023         if (msginfo->folder->prefs->enable_default_account)
12024                 account = account_find_from_id(msginfo->folder->prefs->default_account);
12025                 
12026         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
12027                 gchar *to;
12028                 Xstrdup_a(to, msginfo->to, return NULL);
12029                 extract_address(to);
12030                 account = account_find_from_address(to, FALSE);
12031         }
12032
12033         if (!account && prefs_common.forward_account_autosel) {
12034                 gchar *cc = NULL;
12035                 if (!procheader_get_header_from_msginfo
12036                                 (msginfo, &cc, "Cc:")) { 
12037                         gchar *buf = cc + strlen("Cc:");
12038                         extract_address(buf);
12039                         account = account_find_from_address(buf, FALSE);
12040                         g_free(cc);
12041                 }
12042         }
12043         
12044         if (!account && prefs_common.forward_account_autosel) {
12045                 gchar *deliveredto = NULL;
12046                 if (!procheader_get_header_from_msginfo
12047                                 (msginfo, &deliveredto, "Delivered-To:")) { 
12048                         gchar *buf = deliveredto + strlen("Delivered-To:");
12049                         extract_address(buf);
12050                         account = account_find_from_address(buf, FALSE);
12051                         g_free(deliveredto);
12052                 }
12053         }
12054
12055         if (!account)
12056                 account = msginfo->folder->folder->account;
12057         
12058         return account;
12059 }
12060
12061 gboolean compose_close(Compose *compose)
12062 {
12063         gint x, y;
12064
12065         cm_return_val_if_fail(compose, FALSE);
12066
12067         if (!g_mutex_trylock(compose->mutex)) {
12068                 /* we have to wait for the (possibly deferred by auto-save)
12069                  * drafting to be done, before destroying the compose under
12070                  * it. */
12071                 debug_print("waiting for drafting to finish...\n");
12072                 compose_allow_user_actions(compose, FALSE);
12073                 if (compose->close_timeout_tag == 0) {
12074                         compose->close_timeout_tag = 
12075                                 g_timeout_add (500, (GSourceFunc) compose_close,
12076                                 compose);
12077                 }
12078                 return TRUE;
12079         }
12080
12081         if (compose->draft_timeout_tag >= 0) {
12082                 g_source_remove(compose->draft_timeout_tag);
12083                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
12084         }
12085
12086         gtkut_widget_get_uposition(compose->window, &x, &y);
12087         if (!compose->batch) {
12088                 prefs_common.compose_x = x;
12089                 prefs_common.compose_y = y;
12090         }
12091         g_mutex_unlock(compose->mutex);
12092         compose_destroy(compose);
12093         return FALSE;
12094 }
12095
12096 /**
12097  * Add entry field for each address in list.
12098  * \param compose     E-Mail composition object.
12099  * \param listAddress List of (formatted) E-Mail addresses.
12100  */
12101 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
12102         GList *node;
12103         gchar *addr;
12104         node = listAddress;
12105         while( node ) {
12106                 addr = ( gchar * ) node->data;
12107                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
12108                 node = g_list_next( node );
12109         }
12110 }
12111
12112 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
12113                                     guint action, gboolean opening_multiple)
12114 {
12115         gchar *body = NULL;
12116         GSList *new_msglist = NULL;
12117         MsgInfo *tmp_msginfo = NULL;
12118         gboolean originally_enc = FALSE;
12119         gboolean originally_sig = FALSE;
12120         Compose *compose = NULL;
12121         gchar *s_system = NULL;
12122
12123         cm_return_if_fail(msgview != NULL);
12124
12125         cm_return_if_fail(msginfo_list != NULL);
12126
12127         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
12128                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
12129                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
12130
12131                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
12132                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
12133                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
12134                                                 orig_msginfo, mimeinfo);
12135                         if (tmp_msginfo != NULL) {
12136                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
12137
12138                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
12139                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
12140                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
12141
12142                                 tmp_msginfo->folder = orig_msginfo->folder;
12143                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
12144                                 if (orig_msginfo->tags) {
12145                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
12146                                         tmp_msginfo->folder->tags_dirty = TRUE;
12147                                 }
12148                         }
12149                 }
12150         }
12151
12152         if (!opening_multiple)
12153                 body = messageview_get_selection(msgview);
12154
12155         if (new_msglist) {
12156                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
12157                 procmsg_msginfo_free(&tmp_msginfo);
12158                 g_slist_free(new_msglist);
12159         } else
12160                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
12161
12162         if (compose && originally_enc) {
12163                 compose_force_encryption(compose, compose->account, FALSE, s_system);
12164         }
12165
12166         if (compose && originally_sig && compose->account->default_sign_reply) {
12167                 compose_force_signing(compose, compose->account, s_system);
12168         }
12169         g_free(s_system);
12170         g_free(body);
12171         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
12172 }
12173
12174 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
12175                                     guint action)
12176 {
12177         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
12178         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
12179                 GSList *cur = msginfo_list;
12180                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
12181                                                "messages. Opening the windows "
12182                                                "could take some time. Do you "
12183                                                "want to continue?"), 
12184                                                g_slist_length(msginfo_list));
12185                 if (g_slist_length(msginfo_list) > 9
12186                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_YES, NULL,
12187                         ALERTFOCUS_SECOND) != G_ALERTALTERNATE) {
12188                         g_free(msg);
12189                         return;
12190                 }
12191                 g_free(msg);
12192                 /* We'll open multiple compose windows */
12193                 /* let the WM place the next windows */
12194                 compose_force_window_origin = FALSE;
12195                 for (; cur; cur = cur->next) {
12196                         GSList tmplist;
12197                         tmplist.data = cur->data;
12198                         tmplist.next = NULL;
12199                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
12200                 }
12201                 compose_force_window_origin = TRUE;
12202         } else {
12203                 /* forwarding multiple mails as attachments is done via a
12204                  * single compose window */
12205                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
12206         }
12207 }
12208
12209 void compose_check_for_email_account(Compose *compose)
12210 {
12211         PrefsAccount *ac = NULL, *curr = NULL;
12212         GList *list;
12213         
12214         if (!compose)
12215                 return;
12216
12217         if (compose->account && compose->account->protocol == A_NNTP) {
12218                 ac = account_get_cur_account();
12219                 if (ac->protocol == A_NNTP) {
12220                         list = account_get_list();
12221                         
12222                         for( ; list != NULL ; list = g_list_next(list)) {
12223                                 curr = (PrefsAccount *) list->data;
12224                                 if (curr->protocol != A_NNTP) {
12225                                         ac = curr;
12226                                         break;
12227                                 }
12228                         }
12229                 }
12230                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
12231                                         ac->account_id); 
12232         }
12233 }
12234
12235 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
12236                                 const gchar *address)
12237 {
12238         GSList *msginfo_list = NULL;
12239         gchar *body =  messageview_get_selection(msgview);
12240         Compose *compose;
12241         
12242         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
12243         
12244         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
12245         compose_check_for_email_account(compose);
12246         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
12247         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
12248         compose_reply_set_subject(compose, msginfo);
12249
12250         g_free(body);
12251         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
12252 }
12253
12254 void compose_set_position(Compose *compose, gint pos)
12255 {
12256         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12257
12258         gtkut_text_view_set_position(text, pos);
12259 }
12260
12261 gboolean compose_search_string(Compose *compose,
12262                                 const gchar *str, gboolean case_sens)
12263 {
12264         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12265
12266         return gtkut_text_view_search_string(text, str, case_sens);
12267 }
12268
12269 gboolean compose_search_string_backward(Compose *compose,
12270                                 const gchar *str, gboolean case_sens)
12271 {
12272         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12273
12274         return gtkut_text_view_search_string_backward(text, str, case_sens);
12275 }
12276
12277 /* allocate a msginfo structure and populate its data from a compose data structure */
12278 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
12279 {
12280         MsgInfo *newmsginfo;
12281         GSList *list;
12282         gchar date[RFC822_DATE_BUFFSIZE];
12283
12284         cm_return_val_if_fail( compose != NULL, NULL );
12285
12286         newmsginfo = procmsg_msginfo_new();
12287
12288         /* date is now */
12289         get_rfc822_date(date, sizeof(date));
12290         newmsginfo->date = g_strdup(date);
12291
12292         /* from */
12293         if (compose->from_name) {
12294                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
12295                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
12296         }
12297
12298         /* subject */
12299         if (compose->subject_entry)
12300                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
12301
12302         /* to, cc, reply-to, newsgroups */
12303         for (list = compose->header_list; list; list = list->next) {
12304                 gchar *header = gtk_editable_get_chars(
12305                                                                 GTK_EDITABLE(
12306                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
12307                 gchar *entry = gtk_editable_get_chars(
12308                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
12309
12310                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
12311                         if ( newmsginfo->to == NULL ) {
12312                                 newmsginfo->to = g_strdup(entry);
12313                         } else if (entry && *entry) {
12314                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
12315                                 g_free(newmsginfo->to);
12316                                 newmsginfo->to = tmp;
12317                         }
12318                 } else
12319                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
12320                         if ( newmsginfo->cc == NULL ) {
12321                                 newmsginfo->cc = g_strdup(entry);
12322                         } else if (entry && *entry) {
12323                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
12324                                 g_free(newmsginfo->cc);
12325                                 newmsginfo->cc = tmp;
12326                         }
12327                 } else
12328                 if ( strcasecmp(header,
12329                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12330                         if ( newmsginfo->newsgroups == NULL ) {
12331                                 newmsginfo->newsgroups = g_strdup(entry);
12332                         } else if (entry && *entry) {
12333                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
12334                                 g_free(newmsginfo->newsgroups);
12335                                 newmsginfo->newsgroups = tmp;
12336                         }
12337                 }
12338
12339                 g_free(header);
12340                 g_free(entry);  
12341         }
12342
12343         /* other data is unset */
12344
12345         return newmsginfo;
12346 }
12347
12348 #ifdef USE_ENCHANT
12349 /* update compose's dictionaries from folder dict settings */
12350 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
12351                                                 FolderItem *folder_item)
12352 {
12353         cm_return_if_fail(compose != NULL);
12354
12355         if (compose->gtkaspell && folder_item && folder_item->prefs) {
12356                 FolderItemPrefs *prefs = folder_item->prefs;
12357
12358                 if (prefs->enable_default_dictionary)
12359                         gtkaspell_change_dict(compose->gtkaspell,
12360                                         prefs->default_dictionary, FALSE);
12361                 if (folder_item->prefs->enable_default_alt_dictionary)
12362                         gtkaspell_change_alt_dict(compose->gtkaspell,
12363                                         prefs->default_alt_dictionary);
12364                 if (prefs->enable_default_dictionary
12365                         || prefs->enable_default_alt_dictionary)
12366                         compose_spell_menu_changed(compose);
12367         }
12368 }
12369 #endif
12370
12371 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
12372 {
12373         Compose *compose = (Compose *)data;
12374
12375         cm_return_if_fail(compose != NULL);
12376
12377         gtk_widget_grab_focus(compose->text);
12378 }
12379
12380 static void from_name_activate_cb(GtkWidget *widget, gpointer data)
12381 {
12382         gtk_combo_box_popup(GTK_COMBO_BOX(data));
12383 }
12384
12385
12386 /*
12387  * End of Source.
12388  */