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