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