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