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