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