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