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