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