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