2009-04-03 [colin] 3.7.1cvs37
[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_template_apply              (Compose        *compose,
209                                                  Template       *tmpl,
210                                                  gboolean        replace);
211 static void compose_destroy                     (Compose        *compose);
212
213 static void compose_entries_set                 (Compose        *compose,
214                                                  const gchar    *mailto,
215                                                  ComposeEntryType to_type);
216 static gint compose_parse_header                (Compose        *compose,
217                                                  MsgInfo        *msginfo);
218 static gchar *compose_parse_references          (const gchar    *ref,
219                                                  const gchar    *msgid);
220
221 static gchar *compose_quote_fmt                 (Compose        *compose,
222                                                  MsgInfo        *msginfo,
223                                                  const gchar    *fmt,
224                                                  const gchar    *qmark,
225                                                  const gchar    *body,
226                                                  gboolean        rewrap,
227                                                  gboolean        need_unescape,
228                                                  const gchar *err_msg);
229
230 static void compose_reply_set_entry             (Compose        *compose,
231                                                  MsgInfo        *msginfo,
232                                                  gboolean        to_all,
233                                                  gboolean        to_ml,
234                                                  gboolean        to_sender,
235                                                  gboolean
236                                                  followup_and_reply_to);
237 static void compose_reedit_set_entry            (Compose        *compose,
238                                                  MsgInfo        *msginfo);
239
240 static void compose_insert_sig                  (Compose        *compose,
241                                                  gboolean        replace);
242 static ComposeInsertResult compose_insert_file  (Compose        *compose,
243                                                  const gchar    *file);
244
245 static gboolean compose_attach_append           (Compose        *compose,
246                                                  const gchar    *file,
247                                                  const gchar    *type,
248                                                  const gchar    *content_type);
249 static void compose_attach_parts                (Compose        *compose,
250                                                  MsgInfo        *msginfo);
251
252 static gboolean compose_beautify_paragraph      (Compose        *compose,
253                                                  GtkTextIter    *par_iter,
254                                                  gboolean        force);
255 static void compose_wrap_all                    (Compose        *compose);
256 static void compose_wrap_all_full               (Compose        *compose,
257                                                  gboolean        autowrap);
258
259 static void compose_set_title                   (Compose        *compose);
260 static void compose_select_account              (Compose        *compose,
261                                                  PrefsAccount   *account,
262                                                  gboolean        init);
263
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send                     (Compose        *compose); */
266 static gboolean compose_check_for_valid_recipient
267                                                 (Compose        *compose);
268 static gboolean compose_check_entries           (Compose        *compose,
269                                                  gboolean       check_everything);
270 static gint compose_write_to_file               (Compose        *compose,
271                                                  FILE           *fp,
272                                                  gint            action,
273                                                  gboolean        attach_parts);
274 static gint compose_write_body_to_file          (Compose        *compose,
275                                                  const gchar    *file);
276 static gint compose_remove_reedit_target        (Compose        *compose,
277                                                  gboolean        force);
278 static void compose_remove_draft                        (Compose        *compose);
279 static gint compose_queue_sub                   (Compose        *compose,
280                                                  gint           *msgnum,
281                                                  FolderItem     **item,
282                                                  gchar          **msgpath,
283                                                  gboolean       check_subject,
284                                                  gboolean       remove_reedit_target);
285 static void compose_add_attachments             (Compose        *compose,
286                                                  MimeInfo       *parent);
287 static gchar *compose_get_header                (Compose        *compose);
288
289 static void compose_convert_header              (Compose        *compose,
290                                                  gchar          *dest,
291                                                  gint            len,
292                                                  gchar          *src,
293                                                  gint            header_len,
294                                                  gboolean        addr_field);
295
296 static void compose_attach_info_free            (AttachInfo     *ainfo);
297 static void compose_attach_remove_selected      (GtkAction      *action,
298                                                  gpointer        data);
299
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
500 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
501
502 static void compose_allow_user_actions (Compose *compose, gboolean allow);
503
504 static void compose_nothing_cb             (GtkAction *action, gpointer data)
505 {
506
507 }
508
509 #if USE_ENCHANT
510 static void compose_check_all              (GtkAction *action, gpointer data);
511 static void compose_highlight_all          (GtkAction *action, gpointer data);
512 static void compose_check_backwards        (GtkAction *action, gpointer data);
513 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
514 #endif
515
516 static gint compose_defer_auto_save_draft       (Compose        *compose);
517 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
518
519 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
520
521 #ifdef USE_ENCHANT
522 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
523                                                 FolderItem *folder_item);
524 #endif
525 static void compose_attach_update_label(Compose *compose);
526
527 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
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                                                   _("New message body format 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(_("Message reply From format error."));
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                                           _("Message reply format 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(_("Message forward From format error."));
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                                           _("Message forward format 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(_("Message forward From format error."));
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                                           _("Message redirect format 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(_("Quote mark format error."));
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                 generate_msgid(buf, sizeof(buf));
5067                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5068                 compose->msgid = g_strdup(buf);
5069         } else {
5070                 compose->msgid = NULL;
5071         }
5072
5073         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5074                 return -1;
5075
5076         /* separator between header and body */
5077         err |= (fputs("\n", fp) == EOF);
5078
5079         return (err ? -1:0);
5080 }
5081
5082 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5083 {
5084         FILE *fp;
5085         size_t len;
5086         gchar buf[BUFFSIZE];
5087         int i = 0;
5088         gboolean skip = FALSE;
5089         gboolean err = FALSE;
5090         gchar *not_included[]={
5091                 "Return-Path:",         "Delivered-To:",        "Received:",
5092                 "Subject:",             "X-UIDL:",              "AF:",
5093                 "NF:",                  "PS:",                  "SRH:",
5094                 "SFN:",                 "DSR:",                 "MID:",
5095                 "CFG:",                 "PT:",                  "S:",
5096                 "RQ:",                  "SSV:",                 "NSV:",
5097                 "SSH:",                 "R:",                   "MAID:",
5098                 "NAID:",                "RMID:",                "FMID:",
5099                 "SCF:",                 "RRCPT:",               "NG:",
5100                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5101                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5102                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5103                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5104                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5105                 NULL
5106                 };
5107         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5108                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5109                 return -1;
5110         }
5111
5112         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5113                 skip = FALSE;
5114                 for (i = 0; not_included[i] != NULL; i++) {
5115                         if (g_ascii_strncasecmp(buf, not_included[i],
5116                                                 strlen(not_included[i])) == 0) {
5117                                 skip = TRUE;
5118                                 break;
5119                         }
5120                 }
5121                 if (skip)
5122                         continue;
5123                 if (fputs(buf, fdest) == -1)
5124                         goto error;
5125
5126                 if (!prefs_common.redirect_keep_from) {
5127                         if (g_ascii_strncasecmp(buf, "From:",
5128                                           strlen("From:")) == 0) {
5129                                 err |= (fputs(" (by way of ", fdest) == EOF);
5130                                 if (compose->account->name
5131                                     && *compose->account->name) {
5132                                         compose_convert_header
5133                                                 (compose, buf, sizeof(buf),
5134                                                  compose->account->name,
5135                                                  strlen("From: "),
5136                                                  FALSE);
5137                                         err |= (fprintf(fdest, "%s <%s>",
5138                                                 buf,
5139                                                 compose->account->address) < 0);
5140                                 } else
5141                                         err |= (fprintf(fdest, "%s",
5142                                                 compose->account->address) < 0);
5143                                 err |= (fputs(")", fdest) == EOF);
5144                         }
5145                 }
5146
5147                 if (fputs("\n", fdest) == -1)
5148                         goto error;
5149         }
5150
5151         if (err)
5152                 goto error;
5153
5154         if (compose_redirect_write_headers(compose, fdest))
5155                 goto error;
5156
5157         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5158                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5159                         goto error;
5160         }
5161
5162         fclose(fp);
5163
5164         return 0;
5165 error:
5166         fclose(fp);
5167
5168         return -1;
5169 }
5170
5171 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5172 {
5173         GtkTextBuffer *buffer;
5174         GtkTextIter start, end;
5175         gchar *chars;
5176         gchar *buf;
5177         const gchar *out_codeset;
5178         EncodingType encoding = ENC_UNKNOWN;
5179         MimeInfo *mimemsg, *mimetext;
5180         gint line;
5181         const gchar *src_codeset = CS_INTERNAL;
5182         gchar *from_addr = NULL;
5183         gchar *from_name = NULL;
5184
5185         if (action == COMPOSE_WRITE_FOR_SEND)
5186                 attach_parts = TRUE;
5187
5188         /* create message MimeInfo */
5189         mimemsg = procmime_mimeinfo_new();
5190         mimemsg->type = MIMETYPE_MESSAGE;
5191         mimemsg->subtype = g_strdup("rfc822");
5192         mimemsg->content = MIMECONTENT_MEM;
5193         mimemsg->tmp = TRUE; /* must free content later */
5194         mimemsg->data.mem = compose_get_header(compose);
5195
5196         /* Create text part MimeInfo */
5197         /* get all composed text */
5198         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5199         gtk_text_buffer_get_start_iter(buffer, &start);
5200         gtk_text_buffer_get_end_iter(buffer, &end);
5201         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5202
5203         out_codeset = conv_get_charset_str(compose->out_encoding);
5204
5205         if (!out_codeset && is_ascii_str(chars)) {
5206                 out_codeset = CS_US_ASCII;
5207         } else if (prefs_common.outgoing_fallback_to_ascii &&
5208                    is_ascii_str(chars)) {
5209                 out_codeset = CS_US_ASCII;
5210                 encoding = ENC_7BIT;
5211         }
5212
5213         if (!out_codeset) {
5214                 gchar *test_conv_global_out = NULL;
5215                 gchar *test_conv_reply = NULL;
5216
5217                 /* automatic mode. be automatic. */
5218                 codeconv_set_strict(TRUE);
5219
5220                 out_codeset = conv_get_outgoing_charset_str();
5221                 if (out_codeset) {
5222                         debug_print("trying to convert to %s\n", out_codeset);
5223                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5224                 }
5225
5226                 if (!test_conv_global_out && compose->orig_charset
5227                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5228                         out_codeset = compose->orig_charset;
5229                         debug_print("failure; trying to convert to %s\n", out_codeset);
5230                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5231                 }
5232
5233                 if (!test_conv_global_out && !test_conv_reply) {
5234                         /* we're lost */
5235                         out_codeset = CS_INTERNAL;
5236                         debug_print("failure; finally using %s\n", out_codeset);
5237                 }
5238                 g_free(test_conv_global_out);
5239                 g_free(test_conv_reply);
5240                 codeconv_set_strict(FALSE);
5241         }
5242
5243         if (encoding == ENC_UNKNOWN) {
5244                 if (prefs_common.encoding_method == CTE_BASE64)
5245                         encoding = ENC_BASE64;
5246                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5247                         encoding = ENC_QUOTED_PRINTABLE;
5248                 else if (prefs_common.encoding_method == CTE_8BIT)
5249                         encoding = ENC_8BIT;
5250                 else
5251                         encoding = procmime_get_encoding_for_charset(out_codeset);
5252         }
5253
5254         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5255                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5256
5257         if (action == COMPOSE_WRITE_FOR_SEND) {
5258                 codeconv_set_strict(TRUE);
5259                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5260                 codeconv_set_strict(FALSE);
5261
5262                 if (!buf) {
5263                         AlertValue aval;
5264                         gchar *msg;
5265
5266                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5267                                                 "to the specified %s charset.\n"
5268                                                 "Send it as %s?"), out_codeset, src_codeset);
5269                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5270                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5271                         g_free(msg);
5272
5273                         if (aval != G_ALERTALTERNATE) {
5274                                 g_free(chars);
5275                                 return -3;
5276                         } else {
5277                                 buf = chars;
5278                                 out_codeset = src_codeset;
5279                                 chars = NULL;
5280                         }
5281                 }
5282         } else {
5283                 buf = chars;
5284                 out_codeset = src_codeset;
5285                 chars = NULL;
5286         }
5287         g_free(chars);
5288
5289         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5290                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5291                     strstr(buf, "\nFrom ") != NULL) {
5292                         encoding = ENC_QUOTED_PRINTABLE;
5293                 }
5294         }
5295
5296         mimetext = procmime_mimeinfo_new();
5297         mimetext->content = MIMECONTENT_MEM;
5298         mimetext->tmp = TRUE; /* must free content later */
5299         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5300          * and free the data, which we need later. */
5301         mimetext->data.mem = g_strdup(buf); 
5302         mimetext->type = MIMETYPE_TEXT;
5303         mimetext->subtype = g_strdup("plain");
5304         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5305                             g_strdup(out_codeset));
5306                             
5307         /* protect trailing spaces when signing message */
5308         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5309             privacy_system_can_sign(compose->privacy_system)) {
5310                 encoding = ENC_QUOTED_PRINTABLE;
5311         }
5312         
5313         debug_print("main text: %zd bytes encoded as %s in %d\n",
5314                 strlen(buf), out_codeset, encoding);
5315
5316         /* check for line length limit */
5317         if (action == COMPOSE_WRITE_FOR_SEND &&
5318             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5319             check_line_length(buf, 1000, &line) < 0) {
5320                 AlertValue aval;
5321                 gchar *msg;
5322
5323                 msg = g_strdup_printf
5324                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5325                            "The contents of the message might be broken on the way to the delivery.\n"
5326                            "\n"
5327                            "Send it anyway?"), line + 1);
5328                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5329                 g_free(msg);
5330                 if (aval != G_ALERTALTERNATE) {
5331                         g_free(buf);
5332                         return -1;
5333                 }
5334         }
5335         
5336         if (encoding != ENC_UNKNOWN)
5337                 procmime_encode_content(mimetext, encoding);
5338
5339         /* append attachment parts */
5340         if (compose_use_attach(compose) && attach_parts) {
5341                 MimeInfo *mimempart;
5342                 gchar *boundary = NULL;
5343                 mimempart = procmime_mimeinfo_new();
5344                 mimempart->content = MIMECONTENT_EMPTY;
5345                 mimempart->type = MIMETYPE_MULTIPART;
5346                 mimempart->subtype = g_strdup("mixed");
5347
5348                 do {
5349                         g_free(boundary);
5350                         boundary = generate_mime_boundary(NULL);
5351                 } while (strstr(buf, boundary) != NULL);
5352
5353                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5354                                     boundary);
5355
5356                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5357
5358                 g_node_append(mimempart->node, mimetext->node);
5359                 g_node_append(mimemsg->node, mimempart->node);
5360
5361                 compose_add_attachments(compose, mimempart);
5362         } else
5363                 g_node_append(mimemsg->node, mimetext->node);
5364
5365         g_free(buf);
5366
5367         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5368                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5369                 /* extract name and address */
5370                 if (strstr(spec, " <") && strstr(spec, ">")) {
5371                         from_addr = g_strdup(strrchr(spec, '<')+1);
5372                         *(strrchr(from_addr, '>')) = '\0';
5373                         from_name = g_strdup(spec);
5374                         *(strrchr(from_name, '<')) = '\0';
5375                 } else {
5376                         from_name = NULL;
5377                         from_addr = NULL;
5378                 }
5379                 g_free(spec);
5380         }
5381         /* sign message if sending */
5382         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5383             privacy_system_can_sign(compose->privacy_system))
5384                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5385                         compose->account, from_addr)) {
5386                         g_free(from_name);
5387                         g_free(from_addr);
5388                         return -2;
5389         }
5390         g_free(from_name);
5391         g_free(from_addr);
5392         procmime_write_mimeinfo(mimemsg, fp);
5393         
5394         procmime_mimeinfo_free_all(mimemsg);
5395
5396         return 0;
5397 }
5398
5399 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5400 {
5401         GtkTextBuffer *buffer;
5402         GtkTextIter start, end;
5403         FILE *fp;
5404         size_t len;
5405         gchar *chars, *tmp;
5406
5407         if ((fp = g_fopen(file, "wb")) == NULL) {
5408                 FILE_OP_ERROR(file, "fopen");
5409                 return -1;
5410         }
5411
5412         /* chmod for security */
5413         if (change_file_mode_rw(fp, file) < 0) {
5414                 FILE_OP_ERROR(file, "chmod");
5415                 g_warning("can't change file mode\n");
5416         }
5417
5418         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5419         gtk_text_buffer_get_start_iter(buffer, &start);
5420         gtk_text_buffer_get_end_iter(buffer, &end);
5421         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5422
5423         chars = conv_codeset_strdup
5424                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5425
5426         g_free(tmp);
5427         if (!chars) return -1;
5428
5429         /* write body */
5430         len = strlen(chars);
5431         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5432                 FILE_OP_ERROR(file, "fwrite");
5433                 g_free(chars);
5434                 fclose(fp);
5435                 claws_unlink(file);
5436                 return -1;
5437         }
5438
5439         g_free(chars);
5440
5441         if (fclose(fp) == EOF) {
5442                 FILE_OP_ERROR(file, "fclose");
5443                 claws_unlink(file);
5444                 return -1;
5445         }
5446         return 0;
5447 }
5448
5449 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5450 {
5451         FolderItem *item;
5452         MsgInfo *msginfo = compose->targetinfo;
5453
5454         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5455         if (!msginfo) return -1;
5456
5457         if (!force && MSG_IS_LOCKED(msginfo->flags))
5458                 return 0;
5459
5460         item = msginfo->folder;
5461         cm_return_val_if_fail(item != NULL, -1);
5462
5463         if (procmsg_msg_exist(msginfo) &&
5464             (folder_has_parent_of_type(item, F_QUEUE) ||
5465              folder_has_parent_of_type(item, F_DRAFT) 
5466              || msginfo == compose->autosaved_draft)) {
5467                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5468                         g_warning("can't remove the old message\n");
5469                         return -1;
5470                 } else {
5471                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5472                 }
5473         }
5474
5475         return 0;
5476 }
5477
5478 static void compose_remove_draft(Compose *compose)
5479 {
5480         FolderItem *drafts;
5481         MsgInfo *msginfo = compose->targetinfo;
5482         drafts = account_get_special_folder(compose->account, F_DRAFT);
5483
5484         if (procmsg_msg_exist(msginfo)) {
5485                 folder_item_remove_msg(drafts, msginfo->msgnum);
5486         }
5487
5488 }
5489
5490 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5491                    gboolean remove_reedit_target)
5492 {
5493         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5494 }
5495
5496 static gboolean compose_warn_encryption(Compose *compose)
5497 {
5498         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5499         AlertValue val = G_ALERTALTERNATE;
5500         
5501         if (warning == NULL)
5502                 return TRUE;
5503
5504         val = alertpanel_full(_("Encryption warning"), warning,
5505                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5506                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5507         if (val & G_ALERTDISABLE) {
5508                 val &= ~G_ALERTDISABLE;
5509                 if (val == G_ALERTALTERNATE)
5510                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5511                                 TRUE);
5512         }
5513
5514         if (val == G_ALERTALTERNATE) {
5515                 return TRUE;
5516         } else {
5517                 return FALSE;
5518         } 
5519 }
5520
5521 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5522                               gchar **msgpath, gboolean check_subject,
5523                               gboolean remove_reedit_target)
5524 {
5525         FolderItem *queue;
5526         gchar *tmp;
5527         FILE *fp;
5528         GSList *cur;
5529         gint num;
5530         static gboolean lock = FALSE;
5531         PrefsAccount *mailac = NULL, *newsac = NULL;
5532         gboolean err = FALSE;
5533
5534         debug_print("queueing message...\n");
5535         cm_return_val_if_fail(compose->account != NULL, -1);
5536
5537         lock = TRUE;
5538         
5539         if (compose_check_entries(compose, check_subject) == FALSE) {
5540                 lock = FALSE;
5541                 if (compose->batch) {
5542                         gtk_widget_show_all(compose->window);
5543                 }
5544                 return -1;
5545         }
5546
5547         if (!compose->to_list && !compose->newsgroup_list) {
5548                 g_warning("can't get recipient list.");
5549                 lock = FALSE;
5550                 return -1;
5551         }
5552
5553         if (compose->to_list) {
5554                 if (compose->account->protocol != A_NNTP)
5555                         mailac = compose->account;
5556                 else if (cur_account && cur_account->protocol != A_NNTP)
5557                         mailac = cur_account;
5558                 else if (!(mailac = compose_current_mail_account())) {
5559                         lock = FALSE;
5560                         alertpanel_error(_("No account for sending mails available!"));
5561                         return -1;
5562                 }
5563         }
5564
5565         if (compose->newsgroup_list) {
5566                 if (compose->account->protocol == A_NNTP)
5567                         newsac = compose->account;
5568                 else {
5569                         lock = FALSE;
5570                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5571                         return -1;
5572                 }                       
5573         }
5574
5575         /* write queue header */
5576         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5577                               G_DIR_SEPARATOR, compose, (guint) rand());
5578         debug_print("queuing to %s\n", tmp);
5579         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5580                 FILE_OP_ERROR(tmp, "fopen");
5581                 g_free(tmp);
5582                 lock = FALSE;
5583                 return -2;
5584         }
5585
5586         if (change_file_mode_rw(fp, tmp) < 0) {
5587                 FILE_OP_ERROR(tmp, "chmod");
5588                 g_warning("can't change file mode\n");
5589         }
5590
5591         /* queueing variables */
5592         err |= (fprintf(fp, "AF:\n") < 0);
5593         err |= (fprintf(fp, "NF:0\n") < 0);
5594         err |= (fprintf(fp, "PS:10\n") < 0);
5595         err |= (fprintf(fp, "SRH:1\n") < 0);
5596         err |= (fprintf(fp, "SFN:\n") < 0);
5597         err |= (fprintf(fp, "DSR:\n") < 0);
5598         if (compose->msgid)
5599                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5600         else
5601                 err |= (fprintf(fp, "MID:\n") < 0);
5602         err |= (fprintf(fp, "CFG:\n") < 0);
5603         err |= (fprintf(fp, "PT:0\n") < 0);
5604         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5605         err |= (fprintf(fp, "RQ:\n") < 0);
5606         if (mailac)
5607                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5608         else
5609                 err |= (fprintf(fp, "SSV:\n") < 0);
5610         if (newsac)
5611                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5612         else
5613                 err |= (fprintf(fp, "NSV:\n") < 0);
5614         err |= (fprintf(fp, "SSH:\n") < 0);
5615         /* write recepient list */
5616         if (compose->to_list) {
5617                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5618                 for (cur = compose->to_list->next; cur != NULL;
5619                      cur = cur->next)
5620                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5621                 err |= (fprintf(fp, "\n") < 0);
5622         }
5623         /* write newsgroup list */
5624         if (compose->newsgroup_list) {
5625                 err |= (fprintf(fp, "NG:") < 0);
5626                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5627                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5628                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5629                 err |= (fprintf(fp, "\n") < 0);
5630         }
5631         /* Sylpheed account IDs */
5632         if (mailac)
5633                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5634         if (newsac)
5635                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5636
5637         
5638         if (compose->privacy_system != NULL) {
5639                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5640                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5641                 if (compose->use_encryption) {
5642                         gchar *encdata;
5643                         if (!compose_warn_encryption(compose)) {
5644                                 lock = FALSE;
5645                                 fclose(fp);
5646                                 claws_unlink(tmp);
5647                                 g_free(tmp);
5648                                 return -6;
5649                         }
5650                         if (mailac && mailac->encrypt_to_self) {
5651                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5652                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5653                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5654                                 g_slist_free(tmp_list);
5655                         } else {
5656                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5657                         }
5658                         if (encdata != NULL) {
5659                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5660                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5661                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5662                                                 encdata) < 0);
5663                                 } /* else we finally dont want to encrypt */
5664                         } else {
5665                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5666                                 /* and if encdata was null, it means there's been a problem in 
5667                                  * key selection */
5668                                 lock = FALSE;
5669                                 fclose(fp);
5670                                 claws_unlink(tmp);
5671                                 g_free(tmp);
5672                                 return -5;
5673                         }
5674                         g_free(encdata);
5675                 }
5676         }
5677
5678         /* Save copy folder */
5679         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5680                 gchar *savefolderid;
5681                 
5682                 savefolderid = compose_get_save_to(compose);
5683                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5684                 g_free(savefolderid);
5685         }
5686         /* Save copy folder */
5687         if (compose->return_receipt) {
5688                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5689         }
5690         /* Message-ID of message replying to */
5691         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5692                 gchar *folderid;
5693                 
5694                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5695                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5696                 g_free(folderid);
5697         }
5698         /* Message-ID of message forwarding to */
5699         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5700                 gchar *folderid;
5701                 
5702                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5703                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5704                 g_free(folderid);
5705         }
5706
5707         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5708         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5709
5710         /* end of headers */
5711         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5712
5713         if (compose->redirect_filename != NULL) {
5714                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5715                         lock = FALSE;
5716                         fclose(fp);
5717                         claws_unlink(tmp);
5718                         g_free(tmp);
5719                         return -2;
5720                 }
5721         } else {
5722                 gint result = 0;
5723                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5724                         lock = FALSE;
5725                         fclose(fp);
5726                         claws_unlink(tmp);
5727                         g_free(tmp);
5728                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5729                 }
5730         }
5731         if (err == TRUE) {
5732                 g_warning("failed to write queue message\n");
5733                 fclose(fp);
5734                 claws_unlink(tmp);
5735                 g_free(tmp);
5736                 lock = FALSE;
5737                 return -2;
5738         }
5739         if (fclose(fp) == EOF) {
5740                 FILE_OP_ERROR(tmp, "fclose");
5741                 claws_unlink(tmp);
5742                 g_free(tmp);
5743                 lock = FALSE;
5744                 return -2;
5745         }
5746
5747         if (item && *item) {
5748                 queue = *item;
5749         } else {
5750                 queue = account_get_special_folder(compose->account, F_QUEUE);
5751         }
5752         if (!queue) {
5753                 g_warning("can't find queue folder\n");
5754                 claws_unlink(tmp);
5755                 g_free(tmp);
5756                 lock = FALSE;
5757                 return -1;
5758         }
5759         folder_item_scan(queue);
5760         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5761                 g_warning("can't queue the message\n");
5762                 claws_unlink(tmp);
5763                 g_free(tmp);
5764                 lock = FALSE;
5765                 return -1;
5766         }
5767         
5768         if (msgpath == NULL) {
5769                 claws_unlink(tmp);
5770                 g_free(tmp);
5771         } else
5772                 *msgpath = tmp;
5773
5774         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5775                 compose_remove_reedit_target(compose, FALSE);
5776         }
5777
5778         if ((msgnum != NULL) && (item != NULL)) {
5779                 *msgnum = num;
5780                 *item = queue;
5781         }
5782
5783         return 0;
5784 }
5785
5786 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5787 {
5788         AttachInfo *ainfo;
5789         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5790         MimeInfo *mimepart;
5791         struct stat statbuf;
5792         gchar *type, *subtype;
5793         GtkTreeModel *model;
5794         GtkTreeIter iter;
5795
5796         model = gtk_tree_view_get_model(tree_view);
5797         
5798         if (!gtk_tree_model_get_iter_first(model, &iter))
5799                 return;
5800         do {
5801                 gtk_tree_model_get(model, &iter,
5802                                    COL_DATA, &ainfo,
5803                                    -1);
5804                                                            
5805                 mimepart = procmime_mimeinfo_new();
5806                 mimepart->content = MIMECONTENT_FILE;
5807                 mimepart->data.filename = g_strdup(ainfo->file);
5808                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5809                 mimepart->offset = 0;
5810
5811                 g_stat(ainfo->file, &statbuf);
5812                 mimepart->length = statbuf.st_size;
5813
5814                 type = g_strdup(ainfo->content_type);
5815
5816                 if (!strchr(type, '/')) {
5817                         g_free(type);
5818                         type = g_strdup("application/octet-stream");
5819                 }
5820
5821                 subtype = strchr(type, '/') + 1;
5822                 *(subtype - 1) = '\0';
5823                 mimepart->type = procmime_get_media_type(type);
5824                 mimepart->subtype = g_strdup(subtype);
5825                 g_free(type);
5826
5827                 if (mimepart->type == MIMETYPE_MESSAGE && 
5828                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5829                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5830                 } else {
5831                         if (ainfo->name) {
5832                                 if (mimepart->type == MIMETYPE_APPLICATION && 
5833                                    !strcmp2(mimepart->subtype, "octet-stream"))
5834                                         g_hash_table_insert(mimepart->typeparameters,
5835                                                     g_strdup("name"), g_strdup(ainfo->name));
5836                                 g_hash_table_insert(mimepart->dispositionparameters,
5837                                             g_strdup("filename"), g_strdup(ainfo->name));
5838                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5839                         }
5840                 }
5841
5842                 if (compose->use_signing) {
5843                         if (ainfo->encoding == ENC_7BIT)
5844                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5845                         else if (ainfo->encoding == ENC_8BIT)
5846                                 ainfo->encoding = ENC_BASE64;
5847                 }
5848                 
5849                 procmime_encode_content(mimepart, ainfo->encoding);
5850
5851                 g_node_append(parent->node, mimepart->node);
5852         } while (gtk_tree_model_iter_next(model, &iter));
5853 }
5854
5855 #define IS_IN_CUSTOM_HEADER(header) \
5856         (compose->account->add_customhdr && \
5857          custom_header_find(compose->account->customhdr_list, header) != NULL)
5858
5859 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5860                                                     GString *header, 
5861                                                     const gchar *fieldname,
5862                                                     const gchar *seperator)
5863 {
5864         gchar *str, *fieldname_w_colon;
5865         gboolean add_field = FALSE;
5866         GSList *list;
5867         ComposeHeaderEntry *headerentry;
5868         const gchar *headerentryname;
5869         const gchar *trans_fieldname;
5870         GString *fieldstr;
5871
5872         if (IS_IN_CUSTOM_HEADER(fieldname))
5873                 return;
5874
5875         debug_print("Adding %s-fields\n", fieldname);
5876
5877         fieldstr = g_string_sized_new(64);
5878
5879         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5880         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5881
5882         for (list = compose->header_list; list; list = list->next) {
5883                 headerentry = ((ComposeHeaderEntry *)list->data);
5884                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5885
5886                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5887                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5888                         g_strstrip(str);
5889                         if (str[0] != '\0') {
5890                                 if (add_field)
5891                                         g_string_append(fieldstr, seperator);
5892                                 g_string_append(fieldstr, str);
5893                                 add_field = TRUE;
5894                         }
5895                         g_free(str);
5896                 }
5897         }
5898         if (add_field) {
5899                 gchar *buf;
5900
5901                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5902                 compose_convert_header
5903                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5904                         strlen(fieldname) + 2, TRUE);
5905                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5906                 g_free(buf);
5907         }
5908
5909         g_free(fieldname_w_colon);
5910         g_string_free(fieldstr, TRUE);
5911
5912         return;
5913 }
5914
5915 static gchar *compose_get_header(Compose *compose)
5916 {
5917         gchar buf[BUFFSIZE];
5918         const gchar *entry_str;
5919         gchar *str;
5920         gchar *name;
5921         GSList *list;
5922         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5923         GString *header;
5924         gchar *from_name = NULL, *from_address = NULL;
5925         gchar *tmp;
5926
5927         cm_return_val_if_fail(compose->account != NULL, NULL);
5928         cm_return_val_if_fail(compose->account->address != NULL, NULL);
5929
5930         header = g_string_sized_new(64);
5931
5932         /* Date */
5933         get_rfc822_date(buf, sizeof(buf));
5934         g_string_append_printf(header, "Date: %s\n", buf);
5935
5936         /* From */
5937         
5938         if (compose->account->name && *compose->account->name) {
5939                 gchar *buf;
5940                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5941                 tmp = g_strdup_printf("%s <%s>",
5942                         buf, compose->account->address);
5943         } else {
5944                 tmp = g_strdup_printf("%s",
5945                         compose->account->address);
5946         }
5947         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5948         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5949                 /* use default */
5950                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5951                 from_address = g_strdup(compose->account->address);
5952         } else {
5953                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5954                 /* extract name and address */
5955                 if (strstr(spec, " <") && strstr(spec, ">")) {
5956                         from_address = g_strdup(strrchr(spec, '<')+1);
5957                         *(strrchr(from_address, '>')) = '\0';
5958                         from_name = g_strdup(spec);
5959                         *(strrchr(from_name, '<')) = '\0';
5960                 } else {
5961                         from_name = NULL;
5962                         from_address = g_strdup(spec);
5963                 }
5964                 g_free(spec);
5965         }
5966         g_free(tmp);
5967         
5968         
5969         if (from_name && *from_name) {
5970                 compose_convert_header
5971                         (compose, buf, sizeof(buf), from_name,
5972                          strlen("From: "), TRUE);
5973                 QUOTE_IF_REQUIRED(name, buf);
5974                 
5975                 g_string_append_printf(header, "From: %s <%s>\n",
5976                         name, from_address);
5977         } else
5978                 g_string_append_printf(header, "From: %s\n", from_address);
5979         
5980         g_free(from_name);
5981         g_free(from_address);
5982
5983         /* To */
5984         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5985
5986         /* Newsgroups */
5987         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5988
5989         /* Cc */
5990         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5991
5992         /* Bcc */
5993         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5994
5995         /* Subject */
5996         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5997
5998         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5999                 g_strstrip(str);
6000                 if (*str != '\0') {
6001                         compose_convert_header(compose, buf, sizeof(buf), str,
6002                                                strlen("Subject: "), FALSE);
6003                         g_string_append_printf(header, "Subject: %s\n", buf);
6004                 }
6005         }
6006         g_free(str);
6007
6008         /* Message-ID */
6009         if (compose->account->set_domain && compose->account->domain) {
6010                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
6011         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6012                 g_snprintf(buf, sizeof(buf), "%s", 
6013                         strchr(compose->account->address, '@') ?
6014                                 strchr(compose->account->address, '@')+1 :
6015                                 compose->account->address);
6016         } else {
6017                 g_snprintf(buf, sizeof(buf), "%s", "");
6018         }
6019         
6020         if (compose->account->gen_msgid) {
6021                 generate_msgid(buf, sizeof(buf));
6022                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6023                 compose->msgid = g_strdup(buf);
6024         } else {
6025                 compose->msgid = NULL;
6026         }
6027
6028         if (compose->remove_references == FALSE) {
6029                 /* In-Reply-To */
6030                 if (compose->inreplyto && compose->to_list)
6031                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6032         
6033                 /* References */
6034                 if (compose->references)
6035                         g_string_append_printf(header, "References: %s\n", compose->references);
6036         }
6037
6038         /* Followup-To */
6039         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6040
6041         /* Reply-To */
6042         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6043
6044         /* Organization */
6045         if (compose->account->organization &&
6046             strlen(compose->account->organization) &&
6047             !IS_IN_CUSTOM_HEADER("Organization")) {
6048                 compose_convert_header(compose, buf, sizeof(buf),
6049                                        compose->account->organization,
6050                                        strlen("Organization: "), FALSE);
6051                 g_string_append_printf(header, "Organization: %s\n", buf);
6052         }
6053
6054         /* Program version and system info */
6055         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6056             !compose->newsgroup_list) {
6057                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6058                         prog_version,
6059                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6060                         TARGET_ALIAS);
6061         }
6062         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6063                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6064                         prog_version,
6065                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6066                         TARGET_ALIAS);
6067         }
6068
6069         /* custom headers */
6070         if (compose->account->add_customhdr) {
6071                 GSList *cur;
6072
6073                 for (cur = compose->account->customhdr_list; cur != NULL;
6074                      cur = cur->next) {
6075                         CustomHeader *chdr = (CustomHeader *)cur->data;
6076
6077                         if (custom_header_is_allowed(chdr->name)) {
6078                                 compose_convert_header
6079                                         (compose, buf, sizeof(buf),
6080                                          chdr->value ? chdr->value : "",
6081                                          strlen(chdr->name) + 2, FALSE);
6082                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6083                         }
6084                 }
6085         }
6086
6087         /* Automatic Faces and X-Faces */
6088         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6089                 g_string_append_printf(header, "X-Face: %s\n", buf);
6090         }
6091         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6092                 g_string_append_printf(header, "X-Face: %s\n", buf);
6093         }
6094         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6095                 g_string_append_printf(header, "Face: %s\n", buf);
6096         }
6097         else if (get_default_face (buf, sizeof(buf)) == 0) {
6098                 g_string_append_printf(header, "Face: %s\n", buf);
6099         }
6100
6101         /* PRIORITY */
6102         switch (compose->priority) {
6103                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6104                                                    "X-Priority: 1 (Highest)\n");
6105                         break;
6106                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6107                                                 "X-Priority: 2 (High)\n");
6108                         break;
6109                 case PRIORITY_NORMAL: break;
6110                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6111                                                "X-Priority: 4 (Low)\n");
6112                         break;
6113                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6114                                                   "X-Priority: 5 (Lowest)\n");
6115                         break;
6116                 default: debug_print("compose: priority unknown : %d\n",
6117                                      compose->priority);
6118         }
6119
6120         /* Request Return Receipt */
6121         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6122                 if (compose->return_receipt) {
6123                         if (compose->account->name
6124                             && *compose->account->name) {
6125                                 compose_convert_header(compose, buf, sizeof(buf), 
6126                                                        compose->account->name, 
6127                                                        strlen("Disposition-Notification-To: "),
6128                                                        TRUE);
6129                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6130                         } else
6131                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6132                 }
6133         }
6134
6135         /* get special headers */
6136         for (list = compose->header_list; list; list = list->next) {
6137                 ComposeHeaderEntry *headerentry;
6138                 gchar *tmp;
6139                 gchar *headername;
6140                 gchar *headername_wcolon;
6141                 const gchar *headername_trans;
6142                 gchar *headervalue;
6143                 gchar **string;
6144                 gboolean standard_header = FALSE;
6145
6146                 headerentry = ((ComposeHeaderEntry *)list->data);
6147
6148                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6149                 g_strstrip(tmp);
6150                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6151                         g_free(tmp);
6152                         continue;
6153                 }
6154
6155                 if (!strstr(tmp, ":")) {
6156                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6157                         headername = g_strdup(tmp);
6158                 } else {
6159                         headername_wcolon = g_strdup(tmp);
6160                         headername = g_strdup(strtok(tmp, ":"));
6161                 }
6162                 g_free(tmp);
6163                 
6164                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6165                 Xstrdup_a(headervalue, entry_str, return NULL);
6166                 subst_char(headervalue, '\r', ' ');
6167                 subst_char(headervalue, '\n', ' ');
6168                 string = std_headers;
6169                 while (*string != NULL) {
6170                         headername_trans = prefs_common_translated_header_name(*string);
6171                         if (!strcmp(headername_trans, headername_wcolon))
6172                                 standard_header = TRUE;
6173                         string++;
6174                 }
6175                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6176                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6177                                 
6178                 g_free(headername);
6179                 g_free(headername_wcolon);              
6180         }
6181
6182         str = header->str;
6183         g_string_free(header, FALSE);
6184
6185         return str;
6186 }
6187
6188 #undef IS_IN_CUSTOM_HEADER
6189
6190 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6191                                    gint header_len, gboolean addr_field)
6192 {
6193         gchar *tmpstr = NULL;
6194         const gchar *out_codeset = NULL;
6195
6196         cm_return_if_fail(src != NULL);
6197         cm_return_if_fail(dest != NULL);
6198
6199         if (len < 1) return;
6200
6201         tmpstr = g_strdup(src);
6202
6203         subst_char(tmpstr, '\n', ' ');
6204         subst_char(tmpstr, '\r', ' ');
6205         g_strchomp(tmpstr);
6206
6207         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6208                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6209                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6210                 g_free(tmpstr);
6211                 tmpstr = mybuf;
6212         }
6213
6214         codeconv_set_strict(TRUE);
6215         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6216                 conv_get_charset_str(compose->out_encoding));
6217         codeconv_set_strict(FALSE);
6218         
6219         if (!dest || *dest == '\0') {
6220                 gchar *test_conv_global_out = NULL;
6221                 gchar *test_conv_reply = NULL;
6222
6223                 /* automatic mode. be automatic. */
6224                 codeconv_set_strict(TRUE);
6225
6226                 out_codeset = conv_get_outgoing_charset_str();
6227                 if (out_codeset) {
6228                         debug_print("trying to convert to %s\n", out_codeset);
6229                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6230                 }
6231
6232                 if (!test_conv_global_out && compose->orig_charset
6233                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6234                         out_codeset = compose->orig_charset;
6235                         debug_print("failure; trying to convert to %s\n", out_codeset);
6236                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6237                 }
6238
6239                 if (!test_conv_global_out && !test_conv_reply) {
6240                         /* we're lost */
6241                         out_codeset = CS_INTERNAL;
6242                         debug_print("finally using %s\n", out_codeset);
6243                 }
6244                 g_free(test_conv_global_out);
6245                 g_free(test_conv_reply);
6246                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6247                                         out_codeset);
6248                 codeconv_set_strict(FALSE);
6249         }
6250         g_free(tmpstr);
6251 }
6252
6253 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6254 {
6255         gchar *address;
6256
6257         cm_return_if_fail(user_data != NULL);
6258
6259         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6260         g_strstrip(address);
6261         if (*address != '\0') {
6262                 gchar *name = procheader_get_fromname(address);
6263                 extract_address(address);
6264                 addressbook_add_contact(name, address, NULL, NULL);
6265         }
6266         g_free(address);
6267 }
6268
6269 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6270 {
6271         GtkWidget *menuitem;
6272         gchar *address;
6273
6274         cm_return_if_fail(menu != NULL);
6275         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6276
6277         menuitem = gtk_separator_menu_item_new();
6278         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6279         gtk_widget_show(menuitem);
6280
6281         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6282         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6283
6284         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6285         g_strstrip(address);
6286         if (*address == '\0') {
6287                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6288         }
6289
6290         g_signal_connect(G_OBJECT(menuitem), "activate",
6291                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6292         gtk_widget_show(menuitem);
6293 }
6294
6295 static void compose_create_header_entry(Compose *compose) 
6296 {
6297         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6298
6299         GtkWidget *combo;
6300         GtkWidget *entry;
6301         gchar **string;
6302         const gchar *header = NULL;
6303         ComposeHeaderEntry *headerentry;
6304         gboolean standard_header = FALSE;
6305 #if !(GTK_CHECK_VERSION(2,12,0))
6306         GtkTooltips *tips = compose->tooltips;
6307 #endif
6308         
6309         headerentry = g_new0(ComposeHeaderEntry, 1);
6310
6311         /* Combo box */
6312         combo = gtk_combo_box_entry_new_text();
6313         string = headers; 
6314         while(*string != NULL) {
6315                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6316                         (gchar*)prefs_common_translated_header_name(*string));
6317                 string++;
6318         }
6319         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6320         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6321                          G_CALLBACK(compose_grab_focus_cb), compose);
6322         gtk_widget_show(combo);
6323         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6324                         compose->header_nextrow, compose->header_nextrow+1,
6325                         GTK_SHRINK, GTK_FILL, 0, 0);
6326         if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6327                 const gchar *last_header_entry = gtk_entry_get_text(
6328                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6329                 string = headers;
6330                 while (*string != NULL) {
6331                         if (!strcmp(*string, last_header_entry))
6332                                 standard_header = TRUE;
6333                         string++;
6334                 }
6335                 if (standard_header)
6336                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6337         }
6338         if (!compose->header_last || !standard_header) {
6339                 switch(compose->account->protocol) {
6340                         case A_NNTP:
6341                                 header = prefs_common_translated_header_name("Newsgroups:");
6342                                 break;
6343                         default:
6344                                 header = prefs_common_translated_header_name("To:");
6345                                 break;
6346                 }                                                                   
6347         }
6348         if (header)
6349                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6350
6351         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6352                          G_CALLBACK(compose_grab_focus_cb), compose);
6353
6354         /* Entry field */
6355         entry = gtk_entry_new(); 
6356         gtk_widget_show(entry);
6357         CLAWS_SET_TIP(entry,
6358                 _("Use <tab> to autocomplete from addressbook"));
6359         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6360                         compose->header_nextrow, compose->header_nextrow+1,
6361                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6362
6363         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6364                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6365                          headerentry);
6366         g_signal_connect(G_OBJECT(entry), "changed", 
6367                          G_CALLBACK(compose_headerentry_changed_cb), 
6368                          headerentry);
6369         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6370                          G_CALLBACK(compose_grab_focus_cb), compose);
6371                          
6372         /* email dnd */
6373         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6374                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6375                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6376         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6377                          G_CALLBACK(compose_header_drag_received_cb),
6378                          entry);
6379         g_signal_connect(G_OBJECT(entry), "drag-drop",
6380                          G_CALLBACK(compose_drag_drop),
6381                          compose);
6382         g_signal_connect(G_OBJECT(entry), "populate-popup",
6383                          G_CALLBACK(compose_entry_popup_extend),
6384                          NULL);
6385         
6386         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6387
6388         headerentry->compose = compose;
6389         headerentry->combo = combo;
6390         headerentry->entry = entry;
6391         headerentry->headernum = compose->header_nextrow;
6392
6393         compose->header_nextrow++;
6394         compose->header_last = headerentry;             
6395         compose->header_list =
6396                 g_slist_append(compose->header_list,
6397                                headerentry);
6398 }
6399
6400 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6401 {
6402         ComposeHeaderEntry *last_header;
6403         
6404         last_header = compose->header_last;
6405
6406         gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6407         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6408 }
6409
6410 static void compose_remove_header_entries(Compose *compose) 
6411 {
6412         GSList *list;
6413         for (list = compose->header_list; list; list = list->next) {
6414                 ComposeHeaderEntry *headerentry = 
6415                         (ComposeHeaderEntry *)list->data;
6416                 gtk_widget_destroy(headerentry->combo);
6417                 gtk_widget_destroy(headerentry->entry);
6418                 g_free(headerentry);
6419         }
6420         compose->header_last = NULL;
6421         g_slist_free(compose->header_list);
6422         compose->header_list = NULL;
6423         compose->header_nextrow = 1;
6424         compose_create_header_entry(compose);
6425 }
6426
6427 static GtkWidget *compose_create_header(Compose *compose) 
6428 {
6429         GtkWidget *from_optmenu_hbox;
6430         GtkWidget *header_scrolledwin;
6431         GtkWidget *header_table;
6432
6433         gint count = 0;
6434
6435         /* header labels and entries */
6436         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6437         gtk_widget_show(header_scrolledwin);
6438         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6439
6440         header_table = gtk_table_new(2, 2, FALSE);
6441         gtk_widget_show(header_table);
6442         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6443         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6444         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6445         count = 0;
6446
6447         /* option menu for selecting accounts */
6448         from_optmenu_hbox = compose_account_option_menu_create(compose);
6449         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6450                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6451         count++;
6452
6453         compose->header_table = header_table;
6454         compose->header_list = NULL;
6455         compose->header_nextrow = count;
6456
6457         compose_create_header_entry(compose);
6458
6459         compose->table            = NULL;
6460
6461         return header_scrolledwin ;
6462 }
6463
6464 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6465 {
6466         Compose *compose = (Compose *)data;
6467         GdkEventButton event;
6468         
6469         event.button = 3;
6470         event.time = gtk_get_current_event_time();
6471
6472         return attach_button_pressed(compose->attach_clist, &event, compose);
6473 }
6474
6475 static GtkWidget *compose_create_attach(Compose *compose)
6476 {
6477         GtkWidget *attach_scrwin;
6478         GtkWidget *attach_clist;
6479
6480         GtkListStore *store;
6481         GtkCellRenderer *renderer;
6482         GtkTreeViewColumn *column;
6483         GtkTreeSelection *selection;
6484
6485         /* attachment list */
6486         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6487         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6488                                        GTK_POLICY_AUTOMATIC,
6489                                        GTK_POLICY_AUTOMATIC);
6490         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6491
6492         store = gtk_list_store_new(N_ATTACH_COLS, 
6493                                    G_TYPE_STRING,
6494                                    G_TYPE_STRING,
6495                                    G_TYPE_STRING,
6496                                    G_TYPE_POINTER,
6497                                    G_TYPE_AUTO_POINTER,
6498                                    -1);
6499         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6500                                         (GTK_TREE_MODEL(store)));
6501         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6502         g_object_unref(store);
6503         
6504         renderer = gtk_cell_renderer_text_new();
6505         column = gtk_tree_view_column_new_with_attributes
6506                         (_("Mime type"), renderer, "text", 
6507                          COL_MIMETYPE, NULL);
6508         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6509         
6510         renderer = gtk_cell_renderer_text_new();
6511         column = gtk_tree_view_column_new_with_attributes
6512                         (_("Size"), renderer, "text", 
6513                          COL_SIZE, NULL);
6514         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6515         
6516         renderer = gtk_cell_renderer_text_new();
6517         column = gtk_tree_view_column_new_with_attributes
6518                         (_("Name"), renderer, "text", 
6519                          COL_NAME, NULL);
6520         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6521
6522         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6523                                      prefs_common.use_stripes_everywhere);
6524         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6525         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6526
6527         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6528                          G_CALLBACK(attach_selected), compose);
6529         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6530                          G_CALLBACK(attach_button_pressed), compose);
6531 #ifndef MAEMO
6532         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6533                          G_CALLBACK(popup_attach_button_pressed), compose);
6534 #else
6535         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6536                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6537         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6538                          G_CALLBACK(popup_attach_button_pressed), compose);
6539 #endif
6540         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6541                          G_CALLBACK(attach_key_pressed), compose);
6542
6543         /* drag and drop */
6544         gtk_drag_dest_set(attach_clist,
6545                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6546                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6547                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6548         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6549                          G_CALLBACK(compose_attach_drag_received_cb),
6550                          compose);
6551         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6552                          G_CALLBACK(compose_drag_drop),
6553                          compose);
6554
6555         compose->attach_scrwin = attach_scrwin;
6556         compose->attach_clist  = attach_clist;
6557
6558         return attach_scrwin;
6559 }
6560
6561 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6562 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6563
6564 static GtkWidget *compose_create_others(Compose *compose)
6565 {
6566         GtkWidget *table;
6567         GtkWidget *savemsg_checkbtn;
6568         GtkWidget *savemsg_combo;
6569         GtkWidget *savemsg_select;
6570         
6571         guint rowcount = 0;
6572         gchar *folderidentifier;
6573
6574         /* Table for settings */
6575         table = gtk_table_new(3, 1, FALSE);
6576         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6577         gtk_widget_show(table);
6578         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6579         rowcount = 0;
6580
6581         /* Save Message to folder */
6582         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6583         gtk_widget_show(savemsg_checkbtn);
6584         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6585         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6586                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6587         }
6588         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6589                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6590
6591         savemsg_combo = gtk_combo_box_entry_new_text();
6592         compose->savemsg_checkbtn = savemsg_checkbtn;
6593         compose->savemsg_combo = savemsg_combo;
6594         gtk_widget_show(savemsg_combo);
6595
6596         if (prefs_common.compose_save_to_history)
6597                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6598                                 prefs_common.compose_save_to_history);
6599
6600         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6601         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6602         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6603                          G_CALLBACK(compose_grab_focus_cb), compose);
6604         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6605                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6606                                   (compose->account, F_OUTBOX));
6607                 compose_set_save_to(compose, folderidentifier);
6608                 g_free(folderidentifier);
6609         }
6610
6611         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6612         gtk_widget_show(savemsg_select);
6613         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6614         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6615                          G_CALLBACK(compose_savemsg_select_cb),
6616                          compose);
6617
6618         rowcount++;
6619
6620         return table;   
6621 }
6622
6623 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6624 {
6625         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6626                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6627 }
6628
6629 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6630 {
6631         FolderItem *dest;
6632         gchar * path;
6633
6634         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6635         if (!dest) return;
6636
6637         path = folder_item_get_identifier(dest);
6638
6639         compose_set_save_to(compose, path);
6640         g_free(path);
6641 }
6642
6643 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6644                                   GdkAtom clip, GtkTextIter *insert_place);
6645
6646
6647 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6648                                        Compose *compose)
6649 {
6650         gint prev_autowrap;
6651         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6652 #if USE_ENCHANT
6653         if (event->button == 3) {
6654                 GtkTextIter iter;
6655                 GtkTextIter sel_start, sel_end;
6656                 gboolean stuff_selected;
6657                 gint x, y;
6658                 /* move the cursor to allow GtkAspell to check the word
6659                  * under the mouse */
6660                 if (event->x && event->y) {
6661                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6662                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6663                                 &x, &y);
6664                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6665                                 &iter, x, y);
6666                 } else {
6667                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6668                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6669                 }
6670                 /* get selection */
6671                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6672                                 buffer,
6673                                 &sel_start, &sel_end);
6674
6675                 gtk_text_buffer_place_cursor (buffer, &iter);
6676                 /* reselect stuff */
6677                 if (stuff_selected 
6678                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6679                         gtk_text_buffer_select_range(buffer,
6680                                 &sel_start, &sel_end);
6681                 }
6682                 return FALSE; /* pass the event so that the right-click goes through */
6683         }
6684 #endif
6685         if (event->button == 2) {
6686                 GtkTextIter iter;
6687                 gint x, y;
6688                 BLOCK_WRAP();
6689                 
6690                 /* get the middle-click position to paste at the correct place */
6691                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6692                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6693                         &x, &y);
6694                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6695                         &iter, x, y);
6696                 
6697                 entry_paste_clipboard(compose, text, 
6698                                 prefs_common.linewrap_pastes,
6699                                 GDK_SELECTION_PRIMARY, &iter);
6700                 UNBLOCK_WRAP();
6701                 return TRUE;
6702         }
6703         return FALSE;
6704 }
6705
6706 #if USE_ENCHANT
6707 static void compose_spell_menu_changed(void *data)
6708 {
6709         Compose *compose = (Compose *)data;
6710         GSList *items;
6711         GtkWidget *menuitem;
6712         GtkWidget *parent_item;
6713         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6714         GSList *spell_menu;
6715
6716         if (compose->gtkaspell == NULL)
6717                 return;
6718
6719         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
6720                         "/Menu/Spelling/Options");
6721
6722         /* setting the submenu removes /Spelling/Options from the factory 
6723          * so we need to save it */
6724
6725         if (parent_item == NULL) {
6726                 parent_item = compose->aspell_options_menu;
6727                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6728         } else
6729                 compose->aspell_options_menu = parent_item;
6730
6731         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6732
6733         spell_menu = g_slist_reverse(spell_menu);
6734         for (items = spell_menu;
6735              items; items = items->next) {
6736                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6737                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6738                 gtk_widget_show(GTK_WIDGET(menuitem));
6739         }
6740         g_slist_free(spell_menu);
6741
6742         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6743         gtk_widget_show(parent_item);
6744 }
6745
6746 static void compose_dict_changed(void *data)
6747 {
6748         Compose *compose = (Compose *) data;
6749
6750         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6751                 return;
6752
6753         gtkaspell_highlight_all(compose->gtkaspell);
6754         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6755 }
6756 #endif
6757
6758 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6759 {
6760         Compose *compose = (Compose *)data;
6761         GdkEventButton event;
6762         
6763         event.button = 3;
6764         event.time = gtk_get_current_event_time();
6765         event.x = 0;
6766         event.y = 0;
6767
6768         return text_clicked(compose->text, &event, compose);
6769 }
6770
6771 static gboolean compose_force_window_origin = TRUE;
6772 static Compose *compose_create(PrefsAccount *account,
6773                                                  FolderItem *folder,
6774                                                  ComposeMode mode,
6775                                                  gboolean batch)
6776 {
6777         Compose   *compose;
6778         GtkWidget *window;
6779         GtkWidget *vbox;
6780         GtkWidget *menubar;
6781         GtkWidget *handlebox;
6782
6783         GtkWidget *notebook;
6784         
6785         GtkWidget *attach_hbox;
6786         GtkWidget *attach_lab1;
6787         GtkWidget *attach_lab2;
6788
6789         GtkWidget *vbox2;
6790
6791         GtkWidget *label;
6792         GtkWidget *subject_hbox;
6793         GtkWidget *subject_frame;
6794         GtkWidget *subject_entry;
6795         GtkWidget *subject;
6796         GtkWidget *paned;
6797
6798         GtkWidget *edit_vbox;
6799         GtkWidget *ruler_hbox;
6800         GtkWidget *ruler;
6801         GtkWidget *scrolledwin;
6802         GtkWidget *text;
6803         GtkTextBuffer *buffer;
6804         GtkClipboard *clipboard;
6805         CLAWS_TIP_DECL();
6806
6807         UndoMain *undostruct;
6808
6809         gchar *titles[N_ATTACH_COLS];
6810         GtkWidget *popupmenu;
6811         GtkWidget *tmpl_menu;
6812         GtkActionGroup *action_group = NULL;
6813
6814 #if USE_ENCHANT
6815         GtkAspell * gtkaspell = NULL;
6816 #endif
6817
6818         static GdkGeometry geometry;
6819
6820         cm_return_val_if_fail(account != NULL, NULL);
6821
6822         debug_print("Creating compose window...\n");
6823         compose = g_new0(Compose, 1);
6824
6825         titles[COL_MIMETYPE] = _("MIME type");
6826         titles[COL_SIZE]     = _("Size");
6827         titles[COL_NAME]     = _("Name");
6828
6829         compose->batch = batch;
6830         compose->account = account;
6831         compose->folder = folder;
6832         
6833         compose->mutex = g_mutex_new();
6834         compose->set_cursor_pos = -1;
6835
6836 #if !(GTK_CHECK_VERSION(2,12,0))
6837         compose->tooltips = tips;
6838 #endif
6839
6840         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6841
6842         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6843         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6844
6845         if (!geometry.max_width) {
6846                 geometry.max_width = gdk_screen_width();
6847                 geometry.max_height = gdk_screen_height();
6848         }
6849
6850         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6851                                       &geometry, GDK_HINT_MAX_SIZE);
6852         if (!geometry.min_width) {
6853                 geometry.min_width = 600;
6854                 geometry.min_height = 440;
6855         }
6856         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6857                                       &geometry, GDK_HINT_MIN_SIZE);
6858
6859 #ifndef GENERIC_UMPC    
6860         if (compose_force_window_origin)
6861                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
6862                                  prefs_common.compose_y);
6863 #endif
6864         g_signal_connect(G_OBJECT(window), "delete_event",
6865                          G_CALLBACK(compose_delete_cb), compose);
6866         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6867         gtk_widget_realize(window);
6868
6869         gtkut_widget_set_composer_icon(window);
6870
6871         vbox = gtk_vbox_new(FALSE, 0);
6872         gtk_container_add(GTK_CONTAINER(window), vbox);
6873
6874         compose->ui_manager = gtk_ui_manager_new();
6875         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6876                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
6877         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6878                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6879         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6880                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6881         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6882                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6883         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6884                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6885
6886 #ifndef MAEMO
6887         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6888 #else
6889         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6890 #endif
6891
6892         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6893         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6894 #ifdef USE_ENCHANT
6895         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6896 #endif
6897         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6898         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6899         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6900
6901 /* Compose menu */
6902         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6903         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6904         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6905         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6906         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6907         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6908         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6909         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6910         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6911         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6912
6913 /* Edit menu */
6914         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6915         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6916         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6917
6918         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6919         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6920         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6921
6922         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6923         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6924         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6925         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6926
6927         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6928
6929         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6930         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6931         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6932         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6933         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6934         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6935         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6936         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6937         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6938         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6939         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6940         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6941         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6942         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6943         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6944
6945         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6946
6947         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6948         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6949         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6950         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6951         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6952
6953         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6954
6955         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6956
6957 #if USE_ENCHANT
6958 /* Spelling menu */
6959         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6960         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6961         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6962         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6963         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6964         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6965 #endif
6966
6967 /* Options menu */
6968         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6969         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6970         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6971         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6972         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6973
6974         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6975         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6976         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6977         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6978         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6979
6980         
6981         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6982         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6983         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6984         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6985         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6986         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6987         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6988
6989         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6990         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6991         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6992         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6993         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6994
6995         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6996
6997         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6998         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6999         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7000         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7001         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7002
7003         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7004         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)
7005         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)
7006         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7007
7008         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7009
7010         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7011         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)
7012         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)
7013
7014         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7015
7016         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7017         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)
7018         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7019
7020         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7021         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)
7022         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7023
7024         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7025
7026         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7027         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)
7028         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7029         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7030         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7031
7032         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7033         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)
7034         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)
7035         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7036         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7037
7038         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7039         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7040         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7041         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7042         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7043
7044         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7045         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7046         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)
7047
7048         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7049         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7050         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7051 /* phew. */
7052
7053 /* Tools menu */
7054         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7055         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7056         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7057         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7058         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7059         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7060
7061 /* Help menu */
7062         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7063
7064         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7065         gtk_widget_show_all(menubar);
7066
7067         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7068 #ifndef MAEMO
7069         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7070 #else
7071         hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7072 #endif
7073
7074         if (prefs_common.toolbar_detachable) {
7075                 handlebox = gtk_handle_box_new();
7076         } else {
7077                 handlebox = gtk_hbox_new(FALSE, 0);
7078         }
7079         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7080
7081         gtk_widget_realize(handlebox);
7082 #ifdef MAEMO
7083         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7084                                           (gpointer)compose);
7085 #else
7086         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7087                                           (gpointer)compose);
7088 #endif
7089
7090         vbox2 = gtk_vbox_new(FALSE, 2);
7091         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7092         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7093         
7094         /* Notebook */
7095         notebook = gtk_notebook_new();
7096         gtk_widget_set_size_request(notebook, -1, 130);
7097         gtk_widget_show(notebook);
7098
7099         /* header labels and entries */
7100         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7101                         compose_create_header(compose),
7102                         gtk_label_new_with_mnemonic(_("Hea_der")));
7103         /* attachment list */
7104         attach_hbox = gtk_hbox_new(FALSE, 0);
7105         gtk_widget_show(attach_hbox);
7106         
7107         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7108         gtk_widget_show(attach_lab1);
7109         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7110         
7111         attach_lab2 = gtk_label_new("");
7112         gtk_widget_show(attach_lab2);
7113         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7114         
7115         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7116                         compose_create_attach(compose),
7117                         attach_hbox);
7118         /* Others Tab */
7119         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7120                         compose_create_others(compose),
7121                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7122
7123         /* Subject */
7124         subject_hbox = gtk_hbox_new(FALSE, 0);
7125         gtk_widget_show(subject_hbox);
7126
7127         subject_frame = gtk_frame_new(NULL);
7128         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7129         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7130         gtk_widget_show(subject_frame);
7131
7132         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7133         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7134         gtk_widget_show(subject);
7135
7136         label = gtk_label_new(_("Subject:"));
7137         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7138         gtk_widget_show(label);
7139
7140 #ifdef USE_ENCHANT
7141         subject_entry = claws_spell_entry_new();
7142 #else
7143         subject_entry = gtk_entry_new();
7144 #endif
7145         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7146         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7147                          G_CALLBACK(compose_grab_focus_cb), compose);
7148         gtk_widget_show(subject_entry);
7149         compose->subject_entry = subject_entry;
7150         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7151         
7152         edit_vbox = gtk_vbox_new(FALSE, 0);
7153
7154         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7155
7156         /* ruler */
7157         ruler_hbox = gtk_hbox_new(FALSE, 0);
7158         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7159
7160         ruler = gtk_shruler_new();
7161         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7162         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7163                            BORDER_WIDTH);
7164
7165         /* text widget */
7166         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7167         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7168                                        GTK_POLICY_AUTOMATIC,
7169                                        GTK_POLICY_AUTOMATIC);
7170         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7171                                             GTK_SHADOW_IN);
7172         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7173         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7174
7175         text = gtk_text_view_new();
7176         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7177         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7178         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7179         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7180         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7181         
7182         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7183
7184         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7185                                G_CALLBACK(compose_edit_size_alloc),
7186                                ruler);
7187         g_signal_connect(G_OBJECT(buffer), "changed",
7188                          G_CALLBACK(compose_changed_cb), compose);
7189         g_signal_connect(G_OBJECT(text), "grab_focus",
7190                          G_CALLBACK(compose_grab_focus_cb), compose);
7191         g_signal_connect(G_OBJECT(buffer), "insert_text",
7192                          G_CALLBACK(text_inserted), compose);
7193         g_signal_connect(G_OBJECT(text), "button_press_event",
7194                          G_CALLBACK(text_clicked), compose);
7195 #ifndef MAEMO
7196         g_signal_connect(G_OBJECT(text), "popup-menu",
7197                          G_CALLBACK(compose_popup_menu), compose);
7198 #else
7199         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7200                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7201         g_signal_connect(G_OBJECT(text), "tap-and-hold",
7202                          G_CALLBACK(compose_popup_menu), compose);
7203 #endif
7204         g_signal_connect(G_OBJECT(subject_entry), "changed",
7205                          G_CALLBACK(compose_changed_cb), compose);
7206
7207         /* drag and drop */
7208         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7209                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7210                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7211         g_signal_connect(G_OBJECT(text), "drag_data_received",
7212                          G_CALLBACK(compose_insert_drag_received_cb),
7213                          compose);
7214         g_signal_connect(G_OBJECT(text), "drag-drop",
7215                          G_CALLBACK(compose_drag_drop),
7216                          compose);
7217         gtk_widget_show_all(vbox);
7218
7219         /* pane between attach clist and text */
7220         paned = gtk_vpaned_new();
7221         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7222 #ifdef MAEMO
7223         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7224                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7225         else
7226                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7227 #endif
7228         gtk_paned_add1(GTK_PANED(paned), notebook);
7229         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7230         gtk_widget_show_all(paned);
7231
7232
7233         if (prefs_common.textfont) {
7234                 PangoFontDescription *font_desc;
7235
7236                 font_desc = pango_font_description_from_string
7237                         (prefs_common.textfont);
7238                 if (font_desc) {
7239                         gtk_widget_modify_font(text, font_desc);
7240                         pango_font_description_free(font_desc);
7241                 }
7242         }
7243
7244         gtk_action_group_add_actions(action_group, compose_popup_entries,
7245                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7246         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7247         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7248         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7249         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7250         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7251         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7252         
7253         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7254
7255         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7256         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7257         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7258
7259         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7260
7261         undostruct = undo_init(text);
7262         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7263                                    compose);
7264
7265         address_completion_start(window);
7266
7267         compose->window        = window;
7268         compose->vbox          = vbox;
7269         compose->menubar       = menubar;
7270         compose->handlebox     = handlebox;
7271
7272         compose->vbox2         = vbox2;
7273
7274         compose->paned = paned;
7275
7276         compose->attach_label  = attach_lab2;
7277
7278         compose->notebook      = notebook;
7279         compose->edit_vbox     = edit_vbox;
7280         compose->ruler_hbox    = ruler_hbox;
7281         compose->ruler         = ruler;
7282         compose->scrolledwin   = scrolledwin;
7283         compose->text          = text;
7284
7285         compose->focused_editable = NULL;
7286
7287         compose->popupmenu    = popupmenu;
7288
7289         compose->tmpl_menu = tmpl_menu;
7290
7291         compose->mode = mode;
7292         compose->rmode = mode;
7293
7294         compose->targetinfo = NULL;
7295         compose->replyinfo  = NULL;
7296         compose->fwdinfo    = NULL;
7297
7298         compose->replyto     = NULL;
7299         compose->cc          = NULL;
7300         compose->bcc         = NULL;
7301         compose->followup_to = NULL;
7302
7303         compose->ml_post     = NULL;
7304
7305         compose->inreplyto   = NULL;
7306         compose->references  = NULL;
7307         compose->msgid       = NULL;
7308         compose->boundary    = NULL;
7309
7310         compose->autowrap       = prefs_common.autowrap;
7311         compose->autoindent     = prefs_common.auto_indent;
7312         compose->use_signing    = FALSE;
7313         compose->use_encryption = FALSE;
7314         compose->privacy_system = NULL;
7315
7316         compose->modified = FALSE;
7317
7318         compose->return_receipt = FALSE;
7319
7320         compose->to_list        = NULL;
7321         compose->newsgroup_list = NULL;
7322
7323         compose->undostruct = undostruct;
7324
7325         compose->sig_str = NULL;
7326
7327         compose->exteditor_file    = NULL;
7328         compose->exteditor_pid     = -1;
7329         compose->exteditor_tag     = -1;
7330         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7331
7332 #if USE_ENCHANT
7333         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7334         if (mode != COMPOSE_REDIRECT) {
7335                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7336                     strcmp(prefs_common.dictionary, "")) {
7337                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7338                                                   prefs_common.alt_dictionary,
7339                                                   conv_get_locale_charset_str(),
7340                                                   prefs_common.misspelled_col,
7341                                                   prefs_common.check_while_typing,
7342                                                   prefs_common.recheck_when_changing_dict,
7343                                                   prefs_common.use_alternate,
7344                                                   prefs_common.use_both_dicts,
7345                                                   GTK_TEXT_VIEW(text),
7346                                                   GTK_WINDOW(compose->window),
7347                                                   compose_dict_changed,
7348                                                   compose_spell_menu_changed,
7349                                                   compose);
7350                         if (!gtkaspell) {
7351                                 alertpanel_error(_("Spell checker could not "
7352                                                 "be started.\n%s"),
7353                                                 gtkaspell_checkers_strerror());
7354                                 gtkaspell_checkers_reset_error();
7355                         } else {
7356                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7357                         }
7358                 }
7359         }
7360         compose->gtkaspell = gtkaspell;
7361         compose_spell_menu_changed(compose);
7362         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7363 #endif
7364
7365         compose_select_account(compose, account, TRUE);
7366
7367         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7368         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7369
7370         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7371                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7372
7373         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7374                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7375         
7376         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7377                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7378
7379         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7380         if (account->protocol != A_NNTP)
7381                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7382                                 prefs_common_translated_header_name("To:"));
7383         else
7384                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7385                                 prefs_common_translated_header_name("Newsgroups:"));
7386
7387         addressbook_set_target_compose(compose);
7388         
7389         if (mode != COMPOSE_REDIRECT)
7390                 compose_set_template_menu(compose);
7391         else {
7392                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7393         }
7394
7395         compose_list = g_list_append(compose_list, compose);
7396
7397         if (!prefs_common.show_ruler)
7398                 gtk_widget_hide(ruler_hbox);
7399                 
7400         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7401
7402         /* Priority */
7403         compose->priority = PRIORITY_NORMAL;
7404         compose_update_priority_menu_item(compose);
7405
7406         compose_set_out_encoding(compose);
7407         
7408         /* Actions menu */
7409         compose_update_actions_menu(compose);
7410
7411         /* Privacy Systems menu */
7412         compose_update_privacy_systems_menu(compose);
7413
7414         activate_privacy_system(compose, account, TRUE);
7415         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7416         if (batch) {
7417                 gtk_widget_realize(window);
7418         } else {
7419                 gtk_widget_show(window);
7420 #ifdef MAEMO
7421                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7422                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7423 #endif
7424         }
7425         
7426         return compose;
7427 }
7428
7429 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7430 {
7431         GList *accounts;
7432         GtkWidget *hbox;
7433         GtkWidget *optmenu;
7434         GtkWidget *optmenubox;
7435         GtkListStore *menu;
7436         GtkTreeIter iter;
7437         GtkWidget *from_name = NULL;
7438 #if !(GTK_CHECK_VERSION(2,12,0))
7439         GtkTooltips *tips = compose->tooltips;
7440 #endif
7441
7442         gint num = 0, def_menu = 0;
7443         
7444         accounts = account_get_list();
7445         cm_return_val_if_fail(accounts != NULL, NULL);
7446
7447         optmenubox = gtk_event_box_new();
7448         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7449         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7450
7451         hbox = gtk_hbox_new(FALSE, 6);
7452         from_name = gtk_entry_new();
7453         
7454         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7455                          G_CALLBACK(compose_grab_focus_cb), compose);
7456
7457         for (; accounts != NULL; accounts = accounts->next, num++) {
7458                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7459                 gchar *name, *from = NULL;
7460
7461                 if (ac == compose->account) def_menu = num;
7462
7463                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7464                                        ac->account_name);
7465                 
7466                 if (ac == compose->account) {
7467                         if (ac->name && *ac->name) {
7468                                 gchar *buf;
7469                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7470                                 from = g_strdup_printf("%s <%s>",
7471                                                        buf, ac->address);
7472                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7473                         } else {
7474                                 from = g_strdup_printf("%s",
7475                                                        ac->address);
7476                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7477                         }
7478                 }
7479                 COMBOBOX_ADD(menu, name, ac->account_id);
7480                 g_free(name);
7481                 g_free(from);
7482         }
7483
7484         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7485
7486         g_signal_connect(G_OBJECT(optmenu), "changed",
7487                         G_CALLBACK(account_activated),
7488                         compose);
7489         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7490                          G_CALLBACK(compose_entry_popup_extend),
7491                          NULL);
7492
7493         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7494         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7495         
7496         CLAWS_SET_TIP(optmenubox,
7497                 _("Account to use for this email"));
7498         CLAWS_SET_TIP(from_name,
7499                 _("Sender address to be used"));
7500
7501         compose->from_name = from_name;
7502         
7503         return hbox;
7504 }
7505
7506 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7507 {
7508         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7509         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7510         Compose *compose = (Compose *) data;
7511         if (active) {
7512                 compose->priority = value;
7513         }
7514 }
7515
7516 static void compose_reply_change_mode(Compose *compose,
7517                                     ComposeMode action)
7518 {
7519         gboolean was_modified = compose->modified;
7520
7521         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7522         
7523         cm_return_if_fail(compose->replyinfo != NULL);
7524         
7525         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7526                 ml = TRUE;
7527         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7528                 followup = TRUE;
7529         if (action == COMPOSE_REPLY_TO_ALL)
7530                 all = TRUE;
7531         if (action == COMPOSE_REPLY_TO_SENDER)
7532                 sender = TRUE;
7533         if (action == COMPOSE_REPLY_TO_LIST)
7534                 ml = TRUE;
7535
7536         compose_remove_header_entries(compose);
7537         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7538         if (compose->account->set_autocc && compose->account->auto_cc)
7539                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7540
7541         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7542                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7543         
7544         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7545                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7546         compose_show_first_last_header(compose, TRUE);
7547         compose->modified = was_modified;
7548         compose_set_title(compose);
7549 }
7550
7551 static void compose_reply_change_mode_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         
7557         if (active)
7558                 compose_reply_change_mode(compose, value);
7559 }
7560
7561 static void compose_update_priority_menu_item(Compose * compose)
7562 {
7563         GtkWidget *menuitem = NULL;
7564         switch (compose->priority) {
7565                 case PRIORITY_HIGHEST:
7566                         menuitem = gtk_ui_manager_get_widget
7567                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7568                         break;
7569                 case PRIORITY_HIGH:
7570                         menuitem = gtk_ui_manager_get_widget
7571                                 (compose->ui_manager, "/Menu/Options/Priority/High");
7572                         break;
7573                 case PRIORITY_NORMAL:
7574                         menuitem = gtk_ui_manager_get_widget
7575                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7576                         break;
7577                 case PRIORITY_LOW:
7578                         menuitem = gtk_ui_manager_get_widget
7579                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
7580                         break;
7581                 case PRIORITY_LOWEST:
7582                         menuitem = gtk_ui_manager_get_widget
7583                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7584                         break;
7585         }
7586         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7587 }       
7588
7589 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7590 {
7591         Compose *compose = (Compose *) data;
7592         gchar *systemid;
7593         gboolean can_sign = FALSE, can_encrypt = FALSE;
7594
7595         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7596
7597         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7598                 return;
7599
7600         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7601         g_free(compose->privacy_system);
7602         compose->privacy_system = NULL;
7603         if (systemid != NULL) {
7604                 compose->privacy_system = g_strdup(systemid);
7605
7606                 can_sign = privacy_system_can_sign(systemid);
7607                 can_encrypt = privacy_system_can_encrypt(systemid);
7608         }
7609
7610         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7611
7612         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7613         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7614 }
7615
7616 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7617 {
7618         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7619         GtkWidget *menuitem = NULL;
7620         GList *amenu;
7621         gboolean can_sign = FALSE, can_encrypt = FALSE;
7622         gboolean found = FALSE;
7623
7624         if (compose->privacy_system != NULL) {
7625                 gchar *systemid;
7626                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7627                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7628                 cm_return_if_fail(menuitem != NULL);
7629
7630                 amenu = GTK_MENU_SHELL(menuitem)->children;
7631                 menuitem = NULL;
7632                 while (amenu != NULL) {
7633                         GList *alist = amenu->next;
7634
7635                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7636                         if (systemid != NULL) {
7637                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
7638                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7639                                         menuitem = GTK_WIDGET(amenu->data);
7640
7641                                         can_sign = privacy_system_can_sign(systemid);
7642                                         can_encrypt = privacy_system_can_encrypt(systemid);
7643                                         found = TRUE;
7644                                         break;
7645                                 } 
7646                         } else if (strlen(compose->privacy_system) == 0 && 
7647                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7648                                         menuitem = GTK_WIDGET(amenu->data);
7649
7650                                         can_sign = FALSE;
7651                                         can_encrypt = FALSE;
7652                                         found = TRUE;
7653                                         break;
7654                         }
7655
7656                         amenu = alist;
7657                 }
7658                 if (menuitem != NULL)
7659                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7660                 
7661                 if (warn && !found && strlen(compose->privacy_system)) {
7662                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7663                                   "will not be able to sign or encrypt this message."),
7664                                   compose->privacy_system);
7665                 }
7666         } 
7667
7668         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7669         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7670 }       
7671  
7672 static void compose_set_out_encoding(Compose *compose)
7673 {
7674         CharSet out_encoding;
7675         const gchar *branch = NULL;
7676         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7677
7678         switch(out_encoding) {
7679                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7680                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7681                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7682                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7683                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7684                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7685                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7686                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7687                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7688                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7689                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7690                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7691                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7692                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7693                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7694                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7695                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7696                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7697                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7698                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7699                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7700                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7701                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7702                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7703                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7704                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7705                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7706                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7707                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7708                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7709                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7710                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7711         }
7712         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7713 }
7714
7715 static void compose_set_template_menu(Compose *compose)
7716 {
7717         GSList *tmpl_list, *cur;
7718         GtkWidget *menu;
7719         GtkWidget *item;
7720
7721         tmpl_list = template_get_config();
7722
7723         menu = gtk_menu_new();
7724
7725         gtk_menu_set_accel_group (GTK_MENU (menu), 
7726                 gtk_ui_manager_get_accel_group(compose->ui_manager));
7727         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7728                 Template *tmpl = (Template *)cur->data;
7729                 gchar *accel_path = NULL;
7730                 item = gtk_menu_item_new_with_label(tmpl->name);
7731                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7732                 g_signal_connect(G_OBJECT(item), "activate",
7733                                  G_CALLBACK(compose_template_activate_cb),
7734                                  compose);
7735                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7736                 gtk_widget_show(item);
7737                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7738                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7739                 g_free(accel_path);
7740         }
7741
7742         gtk_widget_show(menu);
7743         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7744 }
7745
7746 void compose_update_actions_menu(Compose *compose)
7747 {
7748         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7749 }
7750
7751 static void compose_update_privacy_systems_menu(Compose *compose)
7752 {
7753         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7754         GSList *systems, *cur;
7755         GtkWidget *widget;
7756         GtkWidget *system_none;
7757         GSList *group;
7758         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7759         GtkWidget *privacy_menu = gtk_menu_new();
7760
7761         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7762         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7763
7764         g_signal_connect(G_OBJECT(system_none), "activate",
7765                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7766
7767         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7768         gtk_widget_show(system_none);
7769
7770         systems = privacy_get_system_ids();
7771         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7772                 gchar *systemid = cur->data;
7773
7774                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7775                 widget = gtk_radio_menu_item_new_with_label(group,
7776                         privacy_system_get_name(systemid));
7777                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7778                                        g_strdup(systemid), g_free);
7779                 g_signal_connect(G_OBJECT(widget), "activate",
7780                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7781
7782                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7783                 gtk_widget_show(widget);
7784                 g_free(systemid);
7785         }
7786         g_slist_free(systems);
7787         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7788         gtk_widget_show_all(privacy_menu);
7789         gtk_widget_show_all(privacy_menuitem);
7790 }
7791
7792 void compose_reflect_prefs_all(void)
7793 {
7794         GList *cur;
7795         Compose *compose;
7796
7797         for (cur = compose_list; cur != NULL; cur = cur->next) {
7798                 compose = (Compose *)cur->data;
7799                 compose_set_template_menu(compose);
7800         }
7801 }
7802
7803 void compose_reflect_prefs_pixmap_theme(void)
7804 {
7805         GList *cur;
7806         Compose *compose;
7807
7808         for (cur = compose_list; cur != NULL; cur = cur->next) {
7809                 compose = (Compose *)cur->data;
7810                 toolbar_update(TOOLBAR_COMPOSE, compose);
7811         }
7812 }
7813
7814 static const gchar *compose_quote_char_from_context(Compose *compose)
7815 {
7816         const gchar *qmark = NULL;
7817
7818         cm_return_val_if_fail(compose != NULL, NULL);
7819
7820         switch (compose->mode) {
7821                 /* use forward-specific quote char */
7822                 case COMPOSE_FORWARD:
7823                 case COMPOSE_FORWARD_AS_ATTACH:
7824                 case COMPOSE_FORWARD_INLINE:
7825                         if (compose->folder && compose->folder->prefs &&
7826                                         compose->folder->prefs->forward_with_format)
7827                                 qmark = compose->folder->prefs->forward_quotemark;
7828                         else if (compose->account->forward_with_format)
7829                                 qmark = compose->account->forward_quotemark;
7830                         else
7831                                 qmark = prefs_common.fw_quotemark;
7832                         break;
7833
7834                 /* use reply-specific quote char in all other modes */
7835                 default:
7836                         if (compose->folder && compose->folder->prefs &&
7837                                         compose->folder->prefs->reply_with_format)
7838                                 qmark = compose->folder->prefs->reply_quotemark;
7839                         else if (compose->account->reply_with_format)
7840                                 qmark = compose->account->reply_quotemark;
7841                         else
7842                                 qmark = prefs_common.quotemark;
7843                         break;
7844         }
7845
7846         if (qmark == NULL || *qmark == '\0')
7847                 qmark = "> ";
7848
7849         return qmark;
7850 }
7851
7852 static void compose_template_apply(Compose *compose, Template *tmpl,
7853                                    gboolean replace)
7854 {
7855         GtkTextView *text;
7856         GtkTextBuffer *buffer;
7857         GtkTextMark *mark;
7858         GtkTextIter iter;
7859         const gchar *qmark;
7860         gchar *parsed_str = NULL;
7861         gint cursor_pos = 0;
7862         const gchar *err_msg = _("Template body format error at line %d.");
7863         if (!tmpl) return;
7864
7865         /* process the body */
7866
7867         text = GTK_TEXT_VIEW(compose->text);
7868         buffer = gtk_text_view_get_buffer(text);
7869
7870         if (tmpl->value) {
7871                 qmark = compose_quote_char_from_context(compose);
7872
7873                 if (compose->replyinfo != NULL) {
7874
7875                         if (replace)
7876                                 gtk_text_buffer_set_text(buffer, "", -1);
7877                         mark = gtk_text_buffer_get_insert(buffer);
7878                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7879
7880                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7881                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7882
7883                 } else if (compose->fwdinfo != NULL) {
7884
7885                         if (replace)
7886                                 gtk_text_buffer_set_text(buffer, "", -1);
7887                         mark = gtk_text_buffer_get_insert(buffer);
7888                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7889
7890                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7891                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7892
7893                 } else {
7894                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7895
7896                         GtkTextIter start, end;
7897                         gchar *tmp = NULL;
7898
7899                         gtk_text_buffer_get_start_iter(buffer, &start);
7900                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7901                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7902
7903                         /* clear the buffer now */
7904                         if (replace)
7905                                 gtk_text_buffer_set_text(buffer, "", -1);
7906
7907                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7908                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7909                         procmsg_msginfo_free( dummyinfo );
7910
7911                         g_free( tmp );
7912                 } 
7913         } else {
7914                 if (replace)
7915                         gtk_text_buffer_set_text(buffer, "", -1);
7916                 mark = gtk_text_buffer_get_insert(buffer);
7917                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7918         }       
7919
7920         if (replace && parsed_str && compose->account->auto_sig)
7921                 compose_insert_sig(compose, FALSE);
7922
7923         if (replace && parsed_str) {
7924                 gtk_text_buffer_get_start_iter(buffer, &iter);
7925                 gtk_text_buffer_place_cursor(buffer, &iter);
7926         }
7927         
7928         if (parsed_str) {
7929                 cursor_pos = quote_fmt_get_cursor_pos();
7930                 compose->set_cursor_pos = cursor_pos;
7931                 if (cursor_pos == -1)
7932                         cursor_pos = 0;
7933                 gtk_text_buffer_get_start_iter(buffer, &iter);
7934                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7935                 gtk_text_buffer_place_cursor(buffer, &iter);
7936         }
7937
7938         /* process the other fields */
7939
7940         compose_template_apply_fields(compose, tmpl);
7941         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7942         quote_fmt_reset_vartable();
7943         compose_changed_cb(NULL, compose);
7944 }
7945
7946 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7947 {
7948         MsgInfo* dummyinfo = NULL;
7949         MsgInfo *msginfo = NULL;
7950         gchar *buf = NULL;
7951
7952         if (compose->replyinfo != NULL)
7953                 msginfo = compose->replyinfo;
7954         else if (compose->fwdinfo != NULL)
7955                 msginfo = compose->fwdinfo;
7956         else {
7957                 dummyinfo = compose_msginfo_new_from_compose(compose);
7958                 msginfo = dummyinfo;
7959         }
7960
7961         if (tmpl->from && *tmpl->from != '\0') {
7962 #ifdef USE_ENCHANT
7963                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7964                                 compose->gtkaspell);
7965 #else
7966                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7967 #endif
7968                 quote_fmt_scan_string(tmpl->from);
7969                 quote_fmt_parse();
7970
7971                 buf = quote_fmt_get_buffer();
7972                 if (buf == NULL) {
7973                         alertpanel_error(_("Template From format error."));
7974                 } else {
7975                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7976                 }
7977         }
7978
7979         if (tmpl->to && *tmpl->to != '\0') {
7980 #ifdef USE_ENCHANT
7981                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7982                                 compose->gtkaspell);
7983 #else
7984                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7985 #endif
7986                 quote_fmt_scan_string(tmpl->to);
7987                 quote_fmt_parse();
7988
7989                 buf = quote_fmt_get_buffer();
7990                 if (buf == NULL) {
7991                         alertpanel_error(_("Template To format error."));
7992                 } else {
7993                         compose_entry_append(compose, buf, COMPOSE_TO);
7994                 }
7995         }
7996
7997         if (tmpl->cc && *tmpl->cc != '\0') {
7998 #ifdef USE_ENCHANT
7999                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8000                                 compose->gtkaspell);
8001 #else
8002                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8003 #endif
8004                 quote_fmt_scan_string(tmpl->cc);
8005                 quote_fmt_parse();
8006
8007                 buf = quote_fmt_get_buffer();
8008                 if (buf == NULL) {
8009                         alertpanel_error(_("Template Cc format error."));
8010                 } else {
8011                         compose_entry_append(compose, buf, COMPOSE_CC);
8012                 }
8013         }
8014
8015         if (tmpl->bcc && *tmpl->bcc != '\0') {
8016 #ifdef USE_ENCHANT
8017                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8018                                 compose->gtkaspell);
8019 #else
8020                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8021 #endif
8022                 quote_fmt_scan_string(tmpl->bcc);
8023                 quote_fmt_parse();
8024
8025                 buf = quote_fmt_get_buffer();
8026                 if (buf == NULL) {
8027                         alertpanel_error(_("Template Bcc format error."));
8028                 } else {
8029                         compose_entry_append(compose, buf, COMPOSE_BCC);
8030                 }
8031         }
8032
8033         /* process the subject */
8034         if (tmpl->subject && *tmpl->subject != '\0') {
8035 #ifdef USE_ENCHANT
8036                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8037                                 compose->gtkaspell);
8038 #else
8039                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8040 #endif
8041                 quote_fmt_scan_string(tmpl->subject);
8042                 quote_fmt_parse();
8043
8044                 buf = quote_fmt_get_buffer();
8045                 if (buf == NULL) {
8046                         alertpanel_error(_("Template subject format error."));
8047                 } else {
8048                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8049                 }
8050         }
8051
8052         procmsg_msginfo_free( dummyinfo );
8053 }
8054
8055 static void compose_destroy(Compose *compose)
8056 {
8057         GtkTextBuffer *buffer;
8058         GtkClipboard *clipboard;
8059
8060         compose_list = g_list_remove(compose_list, compose);
8061
8062         if (compose->updating) {
8063                 debug_print("danger, not destroying anything now\n");
8064                 compose->deferred_destroy = TRUE;
8065                 return;
8066         }
8067         /* NOTE: address_completion_end() does nothing with the window
8068          * however this may change. */
8069         address_completion_end(compose->window);
8070
8071         slist_free_strings(compose->to_list);
8072         g_slist_free(compose->to_list);
8073         slist_free_strings(compose->newsgroup_list);
8074         g_slist_free(compose->newsgroup_list);
8075         slist_free_strings(compose->header_list);
8076         g_slist_free(compose->header_list);
8077
8078         procmsg_msginfo_free(compose->targetinfo);
8079         procmsg_msginfo_free(compose->replyinfo);
8080         procmsg_msginfo_free(compose->fwdinfo);
8081
8082         g_free(compose->replyto);
8083         g_free(compose->cc);
8084         g_free(compose->bcc);
8085         g_free(compose->newsgroups);
8086         g_free(compose->followup_to);
8087
8088         g_free(compose->ml_post);
8089
8090         g_free(compose->inreplyto);
8091         g_free(compose->references);
8092         g_free(compose->msgid);
8093         g_free(compose->boundary);
8094
8095         g_free(compose->redirect_filename);
8096         if (compose->undostruct)
8097                 undo_destroy(compose->undostruct);
8098
8099         g_free(compose->sig_str);
8100
8101         g_free(compose->exteditor_file);
8102
8103         g_free(compose->orig_charset);
8104
8105         g_free(compose->privacy_system);
8106
8107         if (addressbook_get_target_compose() == compose)
8108                 addressbook_set_target_compose(NULL);
8109
8110 #if USE_ENCHANT
8111         if (compose->gtkaspell) {
8112                 gtkaspell_delete(compose->gtkaspell);
8113                 compose->gtkaspell = NULL;
8114         }
8115 #endif
8116
8117         if (!compose->batch) {
8118                 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8119                 prefs_common.compose_height = compose->window->allocation.height;
8120         }
8121
8122         if (!gtk_widget_get_parent(compose->paned))
8123                 gtk_widget_destroy(compose->paned);
8124         gtk_widget_destroy(compose->popupmenu);
8125
8126         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8127         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8128         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8129
8130         gtk_widget_destroy(compose->window);
8131         toolbar_destroy(compose->toolbar);
8132         g_free(compose->toolbar);
8133         g_mutex_free(compose->mutex);
8134         g_free(compose);
8135 }
8136
8137 static void compose_attach_info_free(AttachInfo *ainfo)
8138 {
8139         g_free(ainfo->file);
8140         g_free(ainfo->content_type);
8141         g_free(ainfo->name);
8142         g_free(ainfo);
8143 }
8144
8145 static void compose_attach_update_label(Compose *compose)
8146 {
8147         GtkTreeIter iter;
8148         gint i = 1;
8149         gchar *text;
8150         GtkTreeModel *model;
8151         
8152         if(compose == NULL)
8153                 return;
8154                 
8155         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8156         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8157                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8158                 return;
8159         }
8160         
8161         while(gtk_tree_model_iter_next(model, &iter))
8162                 i++;
8163         
8164         text = g_strdup_printf("(%d)", i);
8165         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8166         g_free(text);
8167 }
8168
8169 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8170 {
8171         Compose *compose = (Compose *)data;
8172         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8173         GtkTreeSelection *selection;
8174         GList *sel, *cur;
8175         GtkTreeModel *model;
8176
8177         selection = gtk_tree_view_get_selection(tree_view);
8178         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8179
8180         if (!sel) 
8181                 return;
8182
8183         for (cur = sel; cur != NULL; cur = cur->next) {
8184                 GtkTreePath *path = cur->data;
8185                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8186                                                 (model, cur->data);
8187                 cur->data = ref;
8188                 gtk_tree_path_free(path);
8189         }
8190
8191         for (cur = sel; cur != NULL; cur = cur->next) {
8192                 GtkTreeRowReference *ref = cur->data;
8193                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8194                 GtkTreeIter iter;
8195
8196                 if (gtk_tree_model_get_iter(model, &iter, path))
8197                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8198                 
8199                 gtk_tree_path_free(path);
8200                 gtk_tree_row_reference_free(ref);
8201         }
8202
8203         g_list_free(sel);
8204         compose_attach_update_label(compose);
8205 }
8206
8207 static struct _AttachProperty
8208 {
8209         GtkWidget *window;
8210         GtkWidget *mimetype_entry;
8211         GtkWidget *encoding_optmenu;
8212         GtkWidget *path_entry;
8213         GtkWidget *filename_entry;
8214         GtkWidget *ok_btn;
8215         GtkWidget *cancel_btn;
8216 } attach_prop;
8217
8218 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8219 {       
8220         gtk_tree_path_free((GtkTreePath *)ptr);
8221 }
8222
8223 static void compose_attach_property(GtkAction *action, gpointer data)
8224 {
8225         Compose *compose = (Compose *)data;
8226         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8227         AttachInfo *ainfo;
8228         GtkComboBox *optmenu;
8229         GtkTreeSelection *selection;
8230         GList *sel;
8231         GtkTreeModel *model;
8232         GtkTreeIter iter;
8233         GtkTreePath *path;
8234         static gboolean cancelled;
8235
8236         /* only if one selected */
8237         selection = gtk_tree_view_get_selection(tree_view);
8238         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8239                 return;
8240
8241         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8242         if (!sel)
8243                 return;
8244
8245         path = (GtkTreePath *) sel->data;
8246         gtk_tree_model_get_iter(model, &iter, path);
8247         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8248         
8249         if (!ainfo) {
8250                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8251                 g_list_free(sel);
8252                 return;
8253         }               
8254         g_list_free(sel);
8255
8256         if (!attach_prop.window)
8257                 compose_attach_property_create(&cancelled);
8258         gtk_widget_grab_focus(attach_prop.ok_btn);
8259         gtk_widget_show(attach_prop.window);
8260         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8261
8262         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8263         if (ainfo->encoding == ENC_UNKNOWN)
8264                 combobox_select_by_data(optmenu, ENC_BASE64);
8265         else
8266                 combobox_select_by_data(optmenu, ainfo->encoding);
8267
8268         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8269                            ainfo->content_type ? ainfo->content_type : "");
8270         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8271                            ainfo->file ? ainfo->file : "");
8272         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8273                            ainfo->name ? ainfo->name : "");
8274
8275         for (;;) {
8276                 const gchar *entry_text;
8277                 gchar *text;
8278                 gchar *cnttype = NULL;
8279                 gchar *file = NULL;
8280                 off_t size = 0;
8281
8282                 cancelled = FALSE;
8283                 gtk_main();
8284
8285                 gtk_widget_hide(attach_prop.window);
8286                 
8287                 if (cancelled) 
8288                         break;
8289
8290                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8291                 if (*entry_text != '\0') {
8292                         gchar *p;
8293
8294                         text = g_strstrip(g_strdup(entry_text));
8295                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8296                                 cnttype = g_strdup(text);
8297                                 g_free(text);
8298                         } else {
8299                                 alertpanel_error(_("Invalid MIME type."));
8300                                 g_free(text);
8301                                 continue;
8302                         }
8303                 }
8304
8305                 ainfo->encoding = combobox_get_active_data(optmenu);
8306
8307                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8308                 if (*entry_text != '\0') {
8309                         if (is_file_exist(entry_text) &&
8310                             (size = get_file_size(entry_text)) > 0)
8311                                 file = g_strdup(entry_text);
8312                         else {
8313                                 alertpanel_error
8314                                         (_("File doesn't exist or is empty."));
8315                                 g_free(cnttype);
8316                                 continue;
8317                         }
8318                 }
8319
8320                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8321                 if (*entry_text != '\0') {
8322                         g_free(ainfo->name);
8323                         ainfo->name = g_strdup(entry_text);
8324                 }
8325
8326                 if (cnttype) {
8327                         g_free(ainfo->content_type);
8328                         ainfo->content_type = cnttype;
8329                 }
8330                 if (file) {
8331                         g_free(ainfo->file);
8332                         ainfo->file = file;
8333                 }
8334                 if (size)
8335                         ainfo->size = (goffset)size;
8336
8337                 /* update tree store */
8338                 text = to_human_readable(ainfo->size);
8339                 gtk_tree_model_get_iter(model, &iter, path);
8340                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8341                                    COL_MIMETYPE, ainfo->content_type,
8342                                    COL_SIZE, text,
8343                                    COL_NAME, ainfo->name,
8344                                    -1);
8345                 
8346                 break;
8347         }
8348
8349         gtk_tree_path_free(path);
8350 }
8351
8352 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8353 { \
8354         label = gtk_label_new(str); \
8355         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8356                          GTK_FILL, 0, 0, 0); \
8357         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8358  \
8359         entry = gtk_entry_new(); \
8360         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8361                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8362 }
8363
8364 static void compose_attach_property_create(gboolean *cancelled)
8365 {
8366         GtkWidget *window;
8367         GtkWidget *vbox;
8368         GtkWidget *table;
8369         GtkWidget *label;
8370         GtkWidget *mimetype_entry;
8371         GtkWidget *hbox;
8372         GtkWidget *optmenu;
8373         GtkListStore *optmenu_menu;
8374         GtkWidget *path_entry;
8375         GtkWidget *filename_entry;
8376         GtkWidget *hbbox;
8377         GtkWidget *ok_btn;
8378         GtkWidget *cancel_btn;
8379         GList     *mime_type_list, *strlist;
8380         GtkTreeIter iter;
8381
8382         debug_print("Creating attach_property window...\n");
8383
8384         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8385         gtk_widget_set_size_request(window, 480, -1);
8386         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8387         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8388         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8389         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8390         g_signal_connect(G_OBJECT(window), "delete_event",
8391                          G_CALLBACK(attach_property_delete_event),
8392                          cancelled);
8393         g_signal_connect(G_OBJECT(window), "key_press_event",
8394                          G_CALLBACK(attach_property_key_pressed),
8395                          cancelled);
8396
8397         vbox = gtk_vbox_new(FALSE, 8);
8398         gtk_container_add(GTK_CONTAINER(window), vbox);
8399
8400         table = gtk_table_new(4, 2, FALSE);
8401         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8402         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8403         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8404
8405         label = gtk_label_new(_("MIME type")); 
8406         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8407                          GTK_FILL, 0, 0, 0); 
8408         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8409         mimetype_entry = gtk_combo_box_entry_new_text(); 
8410         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8411                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8412                          
8413         /* stuff with list */
8414         mime_type_list = procmime_get_mime_type_list();
8415         strlist = NULL;
8416         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8417                 MimeType *type = (MimeType *) mime_type_list->data;
8418                 gchar *tmp;
8419
8420                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8421
8422                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8423                         g_free(tmp);
8424                 else
8425                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8426                                         (GCompareFunc)strcmp2);
8427         }
8428
8429         for (mime_type_list = strlist; mime_type_list != NULL; 
8430                 mime_type_list = mime_type_list->next) {
8431                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8432                 g_free(mime_type_list->data);
8433         }
8434         g_list_free(strlist);
8435         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8436         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8437
8438         label = gtk_label_new(_("Encoding"));
8439         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8440                          GTK_FILL, 0, 0, 0);
8441         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8442
8443         hbox = gtk_hbox_new(FALSE, 0);
8444         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8445                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8446
8447         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8448         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8449
8450         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8451         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8452         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8453         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8454         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8455
8456         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8457
8458         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8459         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8460
8461         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8462                                       &ok_btn, GTK_STOCK_OK,
8463                                       NULL, NULL);
8464         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8465         gtk_widget_grab_default(ok_btn);
8466
8467         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8468                          G_CALLBACK(attach_property_ok),
8469                          cancelled);
8470         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8471                          G_CALLBACK(attach_property_cancel),
8472                          cancelled);
8473
8474         gtk_widget_show_all(vbox);
8475
8476         attach_prop.window           = window;
8477         attach_prop.mimetype_entry   = mimetype_entry;
8478         attach_prop.encoding_optmenu = optmenu;
8479         attach_prop.path_entry       = path_entry;
8480         attach_prop.filename_entry   = filename_entry;
8481         attach_prop.ok_btn           = ok_btn;
8482         attach_prop.cancel_btn       = cancel_btn;
8483 }
8484
8485 #undef SET_LABEL_AND_ENTRY
8486
8487 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8488 {
8489         *cancelled = FALSE;
8490         gtk_main_quit();
8491 }
8492
8493 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8494 {
8495         *cancelled = TRUE;
8496         gtk_main_quit();
8497 }
8498
8499 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8500                                          gboolean *cancelled)
8501 {
8502         *cancelled = TRUE;
8503         gtk_main_quit();
8504
8505         return TRUE;
8506 }
8507
8508 static gboolean attach_property_key_pressed(GtkWidget *widget,
8509                                             GdkEventKey *event,
8510                                             gboolean *cancelled)
8511 {
8512         if (event && event->keyval == GDK_Escape) {
8513                 *cancelled = TRUE;
8514                 gtk_main_quit();
8515         }
8516         if (event && event->keyval == GDK_Return) {
8517                 *cancelled = FALSE;
8518                 gtk_main_quit();
8519         }
8520         return FALSE;
8521 }
8522
8523 static void compose_exec_ext_editor(Compose *compose)
8524 {
8525 #ifdef G_OS_UNIX
8526         gchar *tmp;
8527         pid_t pid;
8528         gint pipe_fds[2];
8529
8530         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8531                               G_DIR_SEPARATOR, compose);
8532
8533         if (pipe(pipe_fds) < 0) {
8534                 perror("pipe");
8535                 g_free(tmp);
8536                 return;
8537         }
8538
8539         if ((pid = fork()) < 0) {
8540                 perror("fork");
8541                 g_free(tmp);
8542                 return;
8543         }
8544
8545         if (pid != 0) {
8546                 /* close the write side of the pipe */
8547                 close(pipe_fds[1]);
8548
8549                 compose->exteditor_file    = g_strdup(tmp);
8550                 compose->exteditor_pid     = pid;
8551
8552                 compose_set_ext_editor_sensitive(compose, FALSE);
8553
8554 #ifndef G_OS_WIN32
8555                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8556 #else
8557                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8558 #endif
8559                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8560                                                         G_IO_IN,
8561                                                         compose_input_cb,
8562                                                         compose);
8563         } else {        /* process-monitoring process */
8564                 pid_t pid_ed;
8565
8566                 if (setpgid(0, 0))
8567                         perror("setpgid");
8568
8569                 /* close the read side of the pipe */
8570                 close(pipe_fds[0]);
8571
8572                 if (compose_write_body_to_file(compose, tmp) < 0) {
8573                         fd_write_all(pipe_fds[1], "2\n", 2);
8574                         _exit(1);
8575                 }
8576
8577                 pid_ed = compose_exec_ext_editor_real(tmp);
8578                 if (pid_ed < 0) {
8579                         fd_write_all(pipe_fds[1], "1\n", 2);
8580                         _exit(1);
8581                 }
8582
8583                 /* wait until editor is terminated */
8584                 waitpid(pid_ed, NULL, 0);
8585
8586                 fd_write_all(pipe_fds[1], "0\n", 2);
8587
8588                 close(pipe_fds[1]);
8589                 _exit(0);
8590         }
8591
8592         g_free(tmp);
8593 #endif /* G_OS_UNIX */
8594 }
8595
8596 #ifdef G_OS_UNIX
8597 static gint compose_exec_ext_editor_real(const gchar *file)
8598 {
8599         gchar buf[1024];
8600         gchar *p;
8601         gchar **cmdline;
8602         pid_t pid;
8603
8604         cm_return_val_if_fail(file != NULL, -1);
8605
8606         if ((pid = fork()) < 0) {
8607                 perror("fork");
8608                 return -1;
8609         }
8610
8611         if (pid != 0) return pid;
8612
8613         /* grandchild process */
8614
8615         if (setpgid(0, getppid()))
8616                 perror("setpgid");
8617
8618         if (prefs_common_get_ext_editor_cmd() &&
8619             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8620             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8621                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8622         } else {
8623                 if (prefs_common_get_ext_editor_cmd())
8624                         g_warning("External editor command-line is invalid: '%s'\n",
8625                                   prefs_common_get_ext_editor_cmd());
8626                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8627         }
8628
8629         cmdline = strsplit_with_quote(buf, " ", 1024);
8630         execvp(cmdline[0], cmdline);
8631
8632         perror("execvp");
8633         g_strfreev(cmdline);
8634
8635         _exit(1);
8636 }
8637
8638 static gboolean compose_ext_editor_kill(Compose *compose)
8639 {
8640         pid_t pgid = compose->exteditor_pid * -1;
8641         gint ret;
8642
8643         ret = kill(pgid, 0);
8644
8645         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8646                 AlertValue val;
8647                 gchar *msg;
8648
8649                 msg = g_strdup_printf
8650                         (_("The external editor is still working.\n"
8651                            "Force terminating the process?\n"
8652                            "process group id: %d"), -pgid);
8653                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8654                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8655                         
8656                 g_free(msg);
8657
8658                 if (val == G_ALERTALTERNATE) {
8659                         g_source_remove(compose->exteditor_tag);
8660                         g_io_channel_shutdown(compose->exteditor_ch,
8661                                               FALSE, NULL);
8662                         g_io_channel_unref(compose->exteditor_ch);
8663
8664                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8665                         waitpid(compose->exteditor_pid, NULL, 0);
8666
8667                         g_warning("Terminated process group id: %d", -pgid);
8668                         g_warning("Temporary file: %s",
8669                                   compose->exteditor_file);
8670
8671                         compose_set_ext_editor_sensitive(compose, TRUE);
8672
8673                         g_free(compose->exteditor_file);
8674                         compose->exteditor_file    = NULL;
8675                         compose->exteditor_pid     = -1;
8676                         compose->exteditor_ch      = NULL;
8677                         compose->exteditor_tag     = -1;
8678                 } else
8679                         return FALSE;
8680         }
8681
8682         return TRUE;
8683 }
8684
8685 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8686                                  gpointer data)
8687 {
8688         gchar buf[3] = "3";
8689         Compose *compose = (Compose *)data;
8690         gsize bytes_read;
8691
8692         debug_print(_("Compose: input from monitoring process\n"));
8693
8694         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8695
8696         g_io_channel_shutdown(source, FALSE, NULL);
8697         g_io_channel_unref(source);
8698
8699         waitpid(compose->exteditor_pid, NULL, 0);
8700
8701         if (buf[0] == '0') {            /* success */
8702                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8703                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8704
8705                 gtk_text_buffer_set_text(buffer, "", -1);
8706                 compose_insert_file(compose, compose->exteditor_file);
8707                 compose_changed_cb(NULL, compose);
8708                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8709
8710                 if (claws_unlink(compose->exteditor_file) < 0)
8711                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8712         } else if (buf[0] == '1') {     /* failed */
8713                 g_warning("Couldn't exec external editor\n");
8714                 if (claws_unlink(compose->exteditor_file) < 0)
8715                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8716         } else if (buf[0] == '2') {
8717                 g_warning("Couldn't write to file\n");
8718         } else if (buf[0] == '3') {
8719                 g_warning("Pipe read failed\n");
8720         }
8721
8722         compose_set_ext_editor_sensitive(compose, TRUE);
8723
8724         g_free(compose->exteditor_file);
8725         compose->exteditor_file    = NULL;
8726         compose->exteditor_pid     = -1;
8727         compose->exteditor_ch      = NULL;
8728         compose->exteditor_tag     = -1;
8729
8730         return FALSE;
8731 }
8732
8733 static void compose_set_ext_editor_sensitive(Compose *compose,
8734                                              gboolean sensitive)
8735 {
8736         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8737         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8738         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8739         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8740         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8741         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8742         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8743
8744         gtk_widget_set_sensitive(compose->text,                       sensitive);
8745         if (compose->toolbar->send_btn)
8746                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8747         if (compose->toolbar->sendl_btn)
8748                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8749         if (compose->toolbar->draft_btn)
8750                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8751         if (compose->toolbar->insert_btn)
8752                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8753         if (compose->toolbar->sig_btn)
8754                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8755         if (compose->toolbar->exteditor_btn)
8756                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8757         if (compose->toolbar->linewrap_current_btn)
8758                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8759         if (compose->toolbar->linewrap_all_btn)
8760                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8761 }
8762 #endif /* G_OS_UNIX */
8763
8764 /**
8765  * compose_undo_state_changed:
8766  *
8767  * Change the sensivity of the menuentries undo and redo
8768  **/
8769 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8770                                        gint redo_state, gpointer data)
8771 {
8772         Compose *compose = (Compose *)data;
8773
8774         switch (undo_state) {
8775         case UNDO_STATE_TRUE:
8776                 if (!undostruct->undo_state) {
8777                         undostruct->undo_state = TRUE;
8778                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8779                 }
8780                 break;
8781         case UNDO_STATE_FALSE:
8782                 if (undostruct->undo_state) {
8783                         undostruct->undo_state = FALSE;
8784                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8785                 }
8786                 break;
8787         case UNDO_STATE_UNCHANGED:
8788                 break;
8789         case UNDO_STATE_REFRESH:
8790                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8791                 break;
8792         default:
8793                 g_warning("Undo state not recognized");
8794                 break;
8795         }
8796
8797         switch (redo_state) {
8798         case UNDO_STATE_TRUE:
8799                 if (!undostruct->redo_state) {
8800                         undostruct->redo_state = TRUE;
8801                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8802                 }
8803                 break;
8804         case UNDO_STATE_FALSE:
8805                 if (undostruct->redo_state) {
8806                         undostruct->redo_state = FALSE;
8807                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8808                 }
8809                 break;
8810         case UNDO_STATE_UNCHANGED:
8811                 break;
8812         case UNDO_STATE_REFRESH:
8813                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8814                 break;
8815         default:
8816                 g_warning("Redo state not recognized");
8817                 break;
8818         }
8819 }
8820
8821 /* callback functions */
8822
8823 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8824  * includes "non-client" (windows-izm) in calculation, so this calculation
8825  * may not be accurate.
8826  */
8827 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8828                                         GtkAllocation *allocation,
8829                                         GtkSHRuler *shruler)
8830 {
8831         if (prefs_common.show_ruler) {
8832                 gint char_width = 0, char_height = 0;
8833                 gint line_width_in_chars;
8834
8835                 gtkut_get_font_size(GTK_WIDGET(widget),
8836                                     &char_width, &char_height);
8837                 line_width_in_chars =
8838                         (allocation->width - allocation->x) / char_width;
8839
8840                 /* got the maximum */
8841                 gtk_ruler_set_range(GTK_RULER(shruler),
8842                                     0.0, line_width_in_chars, 0,
8843                                     /*line_width_in_chars*/ char_width);
8844         }
8845
8846         return TRUE;
8847 }
8848
8849 static void account_activated(GtkComboBox *optmenu, gpointer data)
8850 {
8851         Compose *compose = (Compose *)data;
8852
8853         PrefsAccount *ac;
8854         gchar *folderidentifier;
8855         gint account_id = 0;
8856         GtkTreeModel *menu;
8857         GtkTreeIter iter;
8858
8859         /* Get ID of active account in the combo box */
8860         menu = gtk_combo_box_get_model(optmenu);
8861         gtk_combo_box_get_active_iter(optmenu, &iter);
8862         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8863
8864         ac = account_find_from_id(account_id);
8865         cm_return_if_fail(ac != NULL);
8866
8867         if (ac != compose->account)
8868                 compose_select_account(compose, ac, FALSE);
8869
8870         /* Set message save folder */
8871         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8872                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8873         }
8874         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8875                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8876                            
8877         compose_set_save_to(compose, NULL);
8878         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8879                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8880                                   (compose->account, F_OUTBOX));
8881                 compose_set_save_to(compose, folderidentifier);
8882                 g_free(folderidentifier);
8883         }
8884 }
8885
8886 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8887                             GtkTreeViewColumn *column, Compose *compose)
8888 {
8889         compose_attach_property(NULL, compose);
8890 }
8891
8892 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8893                                       gpointer data)
8894 {
8895         Compose *compose = (Compose *)data;
8896         GtkTreeSelection *attach_selection;
8897         gint attach_nr_selected;
8898         
8899         if (!event) return FALSE;
8900
8901         if (event->button == 3) {
8902                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8903                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8904                         
8905                 if (attach_nr_selected > 0)
8906                 {
8907                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8908                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8909                 } else {
8910                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8911                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8912                 }
8913                         
8914                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8915                                NULL, NULL, event->button, event->time);
8916                 return TRUE;                           
8917         }
8918
8919         return FALSE;
8920 }
8921
8922 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8923                                    gpointer data)
8924 {
8925         Compose *compose = (Compose *)data;
8926
8927         if (!event) return FALSE;
8928
8929         switch (event->keyval) {
8930         case GDK_Delete:
8931                 compose_attach_remove_selected(NULL, compose);
8932                 break;
8933         }
8934         return FALSE;
8935 }
8936
8937 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8938 {
8939         toolbar_comp_set_sensitive(compose, allow);
8940         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8941         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8942 #if USE_ENCHANT
8943         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8944 #endif  
8945         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8946         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8947         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8948         
8949         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8950
8951 }
8952
8953 static void compose_send_cb(GtkAction *action, gpointer data)
8954 {
8955         Compose *compose = (Compose *)data;
8956         
8957         if (prefs_common.work_offline && 
8958             !inc_offline_should_override(TRUE,
8959                 _("Claws Mail needs network access in order "
8960                   "to send this email.")))
8961                 return;
8962         
8963         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8964                 g_source_remove(compose->draft_timeout_tag);
8965                 compose->draft_timeout_tag = -1;
8966         }
8967
8968         compose_send(compose);
8969 }
8970
8971 static void compose_send_later_cb(GtkAction *action, gpointer data)
8972 {
8973         Compose *compose = (Compose *)data;
8974         gint val;
8975
8976         inc_lock();
8977         compose_allow_user_actions(compose, FALSE);
8978         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8979         compose_allow_user_actions(compose, TRUE);
8980         inc_unlock();
8981
8982         if (!val) {
8983                 compose_close(compose);
8984         } else if (val == -1) {
8985                 alertpanel_error(_("Could not queue message."));
8986         } else if (val == -2) {
8987                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8988         } else if (val == -3) {
8989                 if (privacy_peek_error())
8990                 alertpanel_error(_("Could not queue message for sending:\n\n"
8991                                    "Signature failed: %s"), privacy_get_error());
8992         } else if (val == -4) {
8993                 alertpanel_error(_("Could not queue message for sending:\n\n"
8994                                    "Charset conversion failed."));
8995         } else if (val == -5) {
8996                 alertpanel_error(_("Could not queue message for sending:\n\n"
8997                                    "Couldn't get recipient encryption key."));
8998         } else if (val == -6) {
8999                 /* silent error */
9000         }
9001         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9002 }
9003
9004 #define DRAFTED_AT_EXIT "drafted_at_exit"
9005 static void compose_register_draft(MsgInfo *info)
9006 {
9007         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9008                                       DRAFTED_AT_EXIT, NULL);
9009         FILE *fp = g_fopen(filepath, "ab");
9010         
9011         if (fp) {
9012                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9013                                 info->msgnum);
9014                 fclose(fp);
9015         }
9016                 
9017         g_free(filepath);       
9018 }
9019
9020 gboolean compose_draft (gpointer data, guint action) 
9021 {
9022         Compose *compose = (Compose *)data;
9023         FolderItem *draft;
9024         gchar *tmp;
9025         gint msgnum;
9026         MsgFlags flag = {0, 0};
9027         static gboolean lock = FALSE;
9028         MsgInfo *newmsginfo;
9029         FILE *fp;
9030         gboolean target_locked = FALSE;
9031         gboolean err = FALSE;
9032
9033         if (lock) return FALSE;
9034
9035         if (compose->sending)
9036                 return TRUE;
9037
9038         draft = account_get_special_folder(compose->account, F_DRAFT);
9039         cm_return_val_if_fail(draft != NULL, FALSE);
9040         
9041         if (!g_mutex_trylock(compose->mutex)) {
9042                 /* we don't want to lock the mutex once it's available,
9043                  * because as the only other part of compose.c locking
9044                  * it is compose_close - which means once unlocked,
9045                  * the compose struct will be freed */
9046                 debug_print("couldn't lock mutex, probably sending\n");
9047                 return FALSE;
9048         }
9049         
9050         lock = TRUE;
9051
9052         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9053                               G_DIR_SEPARATOR, compose);
9054         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9055                 FILE_OP_ERROR(tmp, "fopen");
9056                 goto warn_err;
9057         }
9058
9059         /* chmod for security */
9060         if (change_file_mode_rw(fp, tmp) < 0) {
9061                 FILE_OP_ERROR(tmp, "chmod");
9062                 g_warning("can't change file mode\n");
9063         }
9064
9065         /* Save draft infos */
9066         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9067         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9068
9069         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9070                 gchar *savefolderid;
9071
9072                 savefolderid = compose_get_save_to(compose);
9073                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9074                 g_free(savefolderid);
9075         }
9076         if (compose->return_receipt) {
9077                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9078         }
9079         if (compose->privacy_system) {
9080                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9081                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9082                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9083         }
9084
9085         /* Message-ID of message replying to */
9086         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9087                 gchar *folderid;
9088                 
9089                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9090                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9091                 g_free(folderid);
9092         }
9093         /* Message-ID of message forwarding to */
9094         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9095                 gchar *folderid;
9096                 
9097                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9098                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9099                 g_free(folderid);
9100         }
9101
9102         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9103         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9104
9105         /* end of headers */
9106         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9107
9108         if (err) {
9109                 fclose(fp);
9110                 goto warn_err;
9111         }
9112
9113         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9114                 fclose(fp);
9115                 goto warn_err;
9116         }
9117         if (fclose(fp) == EOF) {
9118                 goto warn_err;
9119         }
9120         
9121         if (compose->targetinfo) {
9122                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9123                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9124         }
9125         flag.tmp_flags = MSG_DRAFT;
9126
9127         folder_item_scan(draft);
9128         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9129                 MsgInfo *tmpinfo = NULL;
9130                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9131                 if (compose->msgid) {
9132                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9133                 }
9134                 if (tmpinfo) {
9135                         msgnum = tmpinfo->msgnum;
9136                         procmsg_msginfo_free(tmpinfo);
9137                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9138                 } else {
9139                         debug_print("didn't get draft msgnum after scanning\n");
9140                 }
9141         } else {
9142                 debug_print("got draft msgnum %d from adding\n", msgnum);
9143         }
9144         if (msgnum < 0) {
9145 warn_err:
9146                 claws_unlink(tmp);
9147                 g_free(tmp);
9148                 if (action != COMPOSE_AUTO_SAVE) {
9149                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9150                                 alertpanel_error(_("Could not save draft."));
9151                         else {
9152                                 AlertValue val;
9153                                 gtkut_window_popup(compose->window);
9154                                 val = alertpanel_full(_("Could not save draft"),
9155                                         _("Could not save draft.\n"
9156                                         "Do you want to cancel exit or discard this email?"),
9157                                           _("_Cancel exit"), _("_Discard email"), NULL,
9158                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9159                                 if (val == G_ALERTALTERNATE) {
9160                                         lock = FALSE;
9161                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9162                                         compose_close(compose);
9163                                         return TRUE;
9164                                 } else {
9165                                         lock = FALSE;
9166                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9167                                         return FALSE;
9168                                 }
9169                         }
9170                 }
9171                 goto unlock;
9172         }
9173         g_free(tmp);
9174
9175         if (compose->mode == COMPOSE_REEDIT) {
9176                 compose_remove_reedit_target(compose, TRUE);
9177         }
9178
9179         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9180
9181         if (newmsginfo) {
9182                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9183                 if (target_locked)
9184                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9185                 else
9186                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9187                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9188                         procmsg_msginfo_set_flags(newmsginfo, 0,
9189                                                   MSG_HAS_ATTACHMENT);
9190
9191                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9192                         compose_register_draft(newmsginfo);
9193                 }
9194                 procmsg_msginfo_free(newmsginfo);
9195         }
9196         
9197         folder_item_scan(draft);
9198         
9199         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9200                 lock = FALSE;
9201                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9202                 compose_close(compose);
9203                 return TRUE;
9204         } else {
9205                 struct stat s;
9206                 gchar *path;
9207
9208                 path = folder_item_fetch_msg(draft, msgnum);
9209                 if (path == NULL) {
9210                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9211                         goto unlock;
9212                 }
9213                 if (g_stat(path, &s) < 0) {
9214                         FILE_OP_ERROR(path, "stat");
9215                         g_free(path);
9216                         goto unlock;
9217                 }
9218                 g_free(path);
9219
9220                 procmsg_msginfo_free(compose->targetinfo);
9221                 compose->targetinfo = procmsg_msginfo_new();
9222                 compose->targetinfo->msgnum = msgnum;
9223                 compose->targetinfo->size = (goffset)s.st_size;
9224                 compose->targetinfo->mtime = s.st_mtime;
9225                 compose->targetinfo->folder = draft;
9226                 if (target_locked)
9227                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9228                 compose->mode = COMPOSE_REEDIT;
9229                 
9230                 if (action == COMPOSE_AUTO_SAVE) {
9231                         compose->autosaved_draft = compose->targetinfo;
9232                 }
9233                 compose->modified = FALSE;
9234                 compose_set_title(compose);
9235         }
9236 unlock:
9237         lock = FALSE;
9238         g_mutex_unlock(compose->mutex);
9239         return TRUE;
9240 }
9241
9242 void compose_clear_exit_drafts(void)
9243 {
9244         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9245                                       DRAFTED_AT_EXIT, NULL);
9246         if (is_file_exist(filepath))
9247                 claws_unlink(filepath);
9248         
9249         g_free(filepath);
9250 }
9251
9252 void compose_reopen_exit_drafts(void)
9253 {
9254         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9255                                       DRAFTED_AT_EXIT, NULL);
9256         FILE *fp = g_fopen(filepath, "rb");
9257         gchar buf[1024];
9258         
9259         if (fp) {
9260                 while (fgets(buf, sizeof(buf), fp)) {
9261                         gchar **parts = g_strsplit(buf, "\t", 2);
9262                         const gchar *folder = parts[0];
9263                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9264                         
9265                         if (folder && *folder && msgnum > -1) {
9266                                 FolderItem *item = folder_find_item_from_identifier(folder);
9267                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9268                                 if (info)
9269                                         compose_reedit(info, FALSE);
9270                         }
9271                         g_strfreev(parts);
9272                 }       
9273                 fclose(fp);
9274         }       
9275         g_free(filepath);
9276         compose_clear_exit_drafts();
9277 }
9278
9279 static void compose_save_cb(GtkAction *action, gpointer data)
9280 {
9281         Compose *compose = (Compose *)data;
9282         compose_draft(compose, COMPOSE_KEEP_EDITING);
9283         compose->rmode = COMPOSE_REEDIT;
9284 }
9285
9286 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9287 {
9288         if (compose && file_list) {
9289                 GList *tmp;
9290
9291                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9292                         gchar *file = (gchar *) tmp->data;
9293                         gchar *utf8_filename = conv_filename_to_utf8(file);
9294                         compose_attach_append(compose, file, utf8_filename, NULL);
9295                         compose_changed_cb(NULL, compose);
9296                         if (free_data) {
9297                         g_free(file);
9298                                 tmp->data = NULL;
9299                         }
9300                         g_free(utf8_filename);
9301                 }
9302         }
9303 }
9304
9305 static void compose_attach_cb(GtkAction *action, gpointer data)
9306 {
9307         Compose *compose = (Compose *)data;
9308         GList *file_list;
9309
9310         if (compose->redirect_filename != NULL)
9311                 return;
9312
9313         file_list = filesel_select_multiple_files_open(_("Select file"));
9314
9315         if (file_list) {
9316                 compose_attach_from_list(compose, file_list, TRUE);
9317                 g_list_free(file_list);
9318         }
9319 }
9320
9321 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9322 {
9323         Compose *compose = (Compose *)data;
9324         GList *file_list;
9325
9326         file_list = filesel_select_multiple_files_open(_("Select file"));
9327
9328         if (file_list) {
9329                 GList *tmp;
9330
9331                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9332                         gchar *file = (gchar *) tmp->data;
9333                         gchar *filedup = g_strdup(file);
9334                         gchar *shortfile = g_path_get_basename(filedup);
9335                         ComposeInsertResult res;
9336
9337                         res = compose_insert_file(compose, file);
9338                         if (res == COMPOSE_INSERT_READ_ERROR) {
9339                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9340                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9341                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9342                                                    "for the current encoding, insertion may be incorrect."), shortfile);
9343                         }
9344                         g_free(shortfile);
9345                         g_free(filedup);
9346                         g_free(file);
9347                 }
9348                 g_list_free(file_list);
9349         }
9350 }
9351
9352 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9353 {
9354         Compose *compose = (Compose *)data;
9355
9356         compose_insert_sig(compose, FALSE);
9357 }
9358
9359 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9360                               gpointer data)
9361 {
9362         gint x, y;
9363         Compose *compose = (Compose *)data;
9364
9365         gtkut_widget_get_uposition(widget, &x, &y);
9366         if (!compose->batch) {
9367                 prefs_common.compose_x = x;
9368                 prefs_common.compose_y = y;
9369         }
9370         if (compose->sending || compose->updating)
9371                 return TRUE;
9372         compose_close_cb(NULL, compose);
9373         return TRUE;
9374 }
9375
9376 void compose_close_toolbar(Compose *compose)
9377 {
9378         compose_close_cb(NULL, compose);
9379 }
9380
9381 static void compose_close_cb(GtkAction *action, gpointer data)
9382 {
9383         Compose *compose = (Compose *)data;
9384         AlertValue val;
9385
9386 #ifdef G_OS_UNIX
9387         if (compose->exteditor_tag != -1) {
9388                 if (!compose_ext_editor_kill(compose))
9389                         return;
9390         }
9391 #endif
9392
9393         if (compose->modified) {
9394                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9395                 if (!g_mutex_trylock(compose->mutex)) {
9396                         /* we don't want to lock the mutex once it's available,
9397                          * because as the only other part of compose.c locking
9398                          * it is compose_close - which means once unlocked,
9399                          * the compose struct will be freed */
9400                         debug_print("couldn't lock mutex, probably sending\n");
9401                         return;
9402                 }
9403                 if (!reedit) {
9404                         val = alertpanel(_("Discard message"),
9405                                  _("This message has been modified. Discard it?"),
9406                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9407                 } else {
9408                         val = alertpanel(_("Save changes"),
9409                                  _("This message has been modified. Save the latest changes?"),
9410                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9411                 }
9412                 g_mutex_unlock(compose->mutex);
9413                 switch (val) {
9414                 case G_ALERTDEFAULT:
9415                         if (prefs_common.autosave && !reedit)
9416                                 compose_remove_draft(compose);                  
9417                         break;
9418                 case G_ALERTALTERNATE:
9419                         compose_draft(data, COMPOSE_QUIT_EDITING);
9420                         return;
9421                 default:
9422                         return;
9423                 }
9424         }
9425
9426         compose_close(compose);
9427 }
9428
9429 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9430 {
9431         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9432         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9433         Compose *compose = (Compose *) data;
9434
9435         if (active)
9436                 compose->out_encoding = (CharSet)value;
9437 }
9438
9439 static void compose_address_cb(GtkAction *action, gpointer data)
9440 {
9441         Compose *compose = (Compose *)data;
9442
9443         addressbook_open(compose);
9444 }
9445
9446 static void about_show_cb(GtkAction *action, gpointer data)
9447 {
9448         about_show();
9449 }
9450
9451 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9452 {
9453         Compose *compose = (Compose *)data;
9454         Template *tmpl;
9455         gchar *msg;
9456         AlertValue val;
9457
9458         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9459         cm_return_if_fail(tmpl != NULL);
9460
9461         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9462                               tmpl->name);
9463         val = alertpanel(_("Apply template"), msg,
9464                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9465         g_free(msg);
9466
9467         if (val == G_ALERTDEFAULT)
9468                 compose_template_apply(compose, tmpl, TRUE);
9469         else if (val == G_ALERTALTERNATE)
9470                 compose_template_apply(compose, tmpl, FALSE);
9471 }
9472
9473 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9474 {
9475         Compose *compose = (Compose *)data;
9476
9477         compose_exec_ext_editor(compose);
9478 }
9479
9480 static void compose_undo_cb(GtkAction *action, gpointer data)
9481 {
9482         Compose *compose = (Compose *)data;
9483         gboolean prev_autowrap = compose->autowrap;
9484
9485         compose->autowrap = FALSE;
9486         undo_undo(compose->undostruct);
9487         compose->autowrap = prev_autowrap;
9488 }
9489
9490 static void compose_redo_cb(GtkAction *action, gpointer data)
9491 {
9492         Compose *compose = (Compose *)data;
9493         gboolean prev_autowrap = compose->autowrap;
9494         
9495         compose->autowrap = FALSE;
9496         undo_redo(compose->undostruct);
9497         compose->autowrap = prev_autowrap;
9498 }
9499
9500 static void entry_cut_clipboard(GtkWidget *entry)
9501 {
9502         if (GTK_IS_EDITABLE(entry))
9503                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9504         else if (GTK_IS_TEXT_VIEW(entry))
9505                 gtk_text_buffer_cut_clipboard(
9506                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9507                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9508                         TRUE);
9509 }
9510
9511 static void entry_copy_clipboard(GtkWidget *entry)
9512 {
9513         if (GTK_IS_EDITABLE(entry))
9514                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9515         else if (GTK_IS_TEXT_VIEW(entry))
9516                 gtk_text_buffer_copy_clipboard(
9517                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9518                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9519 }
9520
9521 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9522                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9523 {
9524         if (GTK_IS_TEXT_VIEW(entry)) {
9525                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9526                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9527                 GtkTextIter start_iter, end_iter;
9528                 gint start, end;
9529                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9530
9531                 if (contents == NULL)
9532                         return;
9533         
9534                 /* we shouldn't delete the selection when middle-click-pasting, or we
9535                  * can't mid-click-paste our own selection */
9536                 if (clip != GDK_SELECTION_PRIMARY) {
9537                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9538                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9539                 }
9540                 
9541                 if (insert_place == NULL) {
9542                         /* if insert_place isn't specified, insert at the cursor.
9543                          * used for Ctrl-V pasting */
9544                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9545                         start = gtk_text_iter_get_offset(&start_iter);
9546                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9547                 } else {
9548                         /* if insert_place is specified, paste here.
9549                          * used for mid-click-pasting */
9550                         start = gtk_text_iter_get_offset(insert_place);
9551                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9552                         if (prefs_common.primary_paste_unselects)
9553                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9554                 }
9555                 
9556                 if (!wrap) {
9557                         /* paste unwrapped: mark the paste so it's not wrapped later */
9558                         end = start + strlen(contents);
9559                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9560                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9561                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9562                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9563                         /* rewrap paragraph now (after a mid-click-paste) */
9564                         mark_start = gtk_text_buffer_get_insert(buffer);
9565                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9566                         gtk_text_iter_backward_char(&start_iter);
9567                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9568                 }
9569         } else if (GTK_IS_EDITABLE(entry))
9570                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9571
9572         compose->modified = TRUE;
9573 }
9574
9575 static void entry_allsel(GtkWidget *entry)
9576 {
9577         if (GTK_IS_EDITABLE(entry))
9578                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9579         else if (GTK_IS_TEXT_VIEW(entry)) {
9580                 GtkTextIter startiter, enditer;
9581                 GtkTextBuffer *textbuf;
9582
9583                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9584                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9585                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9586
9587                 gtk_text_buffer_move_mark_by_name(textbuf, 
9588                         "selection_bound", &startiter);
9589                 gtk_text_buffer_move_mark_by_name(textbuf, 
9590                         "insert", &enditer);
9591         }
9592 }
9593
9594 static void compose_cut_cb(GtkAction *action, gpointer data)
9595 {
9596         Compose *compose = (Compose *)data;
9597         if (compose->focused_editable 
9598 #ifndef GENERIC_UMPC
9599             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9600 #endif
9601             )
9602                 entry_cut_clipboard(compose->focused_editable);
9603 }
9604
9605 static void compose_copy_cb(GtkAction *action, gpointer data)
9606 {
9607         Compose *compose = (Compose *)data;
9608         if (compose->focused_editable 
9609 #ifndef GENERIC_UMPC
9610             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9611 #endif
9612             )
9613                 entry_copy_clipboard(compose->focused_editable);
9614 }
9615
9616 static void compose_paste_cb(GtkAction *action, gpointer data)
9617 {
9618         Compose *compose = (Compose *)data;
9619         gint prev_autowrap;
9620         GtkTextBuffer *buffer;
9621         BLOCK_WRAP();
9622         if (compose->focused_editable &&
9623             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9624                 entry_paste_clipboard(compose, compose->focused_editable, 
9625                                 prefs_common.linewrap_pastes,
9626                                 GDK_SELECTION_CLIPBOARD, NULL);
9627         UNBLOCK_WRAP();
9628 }
9629
9630 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9631 {
9632         Compose *compose = (Compose *)data;
9633         gint wrap_quote = prefs_common.linewrap_quote;
9634         if (compose->focused_editable 
9635 #ifndef GENERIC_UMPC
9636             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9637 #endif
9638             ) {
9639                 /* let text_insert() (called directly or at a later time
9640                  * after the gtk_editable_paste_clipboard) know that 
9641                  * text is to be inserted as a quotation. implemented
9642                  * by using a simple refcount... */
9643                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9644                                                 G_OBJECT(compose->focused_editable),
9645                                                 "paste_as_quotation"));
9646                 g_object_set_data(G_OBJECT(compose->focused_editable),
9647                                     "paste_as_quotation",
9648                                     GINT_TO_POINTER(paste_as_quotation + 1));
9649                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9650                 entry_paste_clipboard(compose, compose->focused_editable, 
9651                                 prefs_common.linewrap_pastes,
9652                                 GDK_SELECTION_CLIPBOARD, NULL);
9653                 prefs_common.linewrap_quote = wrap_quote;
9654         }
9655 }
9656
9657 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9658 {
9659         Compose *compose = (Compose *)data;
9660         gint prev_autowrap;
9661         GtkTextBuffer *buffer;
9662         BLOCK_WRAP();
9663         if (compose->focused_editable 
9664 #ifndef GENERIC_UMPC
9665             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9666 #endif
9667             )
9668                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9669                         GDK_SELECTION_CLIPBOARD, NULL);
9670         UNBLOCK_WRAP();
9671 }
9672
9673 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9674 {
9675         Compose *compose = (Compose *)data;
9676         gint prev_autowrap;
9677         GtkTextBuffer *buffer;
9678         BLOCK_WRAP();
9679         if (compose->focused_editable 
9680 #ifndef GENERIC_UMPC
9681             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9682 #endif
9683             )
9684                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9685                         GDK_SELECTION_CLIPBOARD, NULL);
9686         UNBLOCK_WRAP();
9687 }
9688
9689 static void compose_allsel_cb(GtkAction *action, gpointer data)
9690 {
9691         Compose *compose = (Compose *)data;
9692         if (compose->focused_editable 
9693 #ifndef GENERIC_UMPC
9694             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9695 #endif
9696             )
9697                 entry_allsel(compose->focused_editable);
9698 }
9699
9700 static void textview_move_beginning_of_line (GtkTextView *text)
9701 {
9702         GtkTextBuffer *buffer;
9703         GtkTextMark *mark;
9704         GtkTextIter ins;
9705
9706         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9707
9708         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9709         mark = gtk_text_buffer_get_insert(buffer);
9710         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9711         gtk_text_iter_set_line_offset(&ins, 0);
9712         gtk_text_buffer_place_cursor(buffer, &ins);
9713 }
9714
9715 static void textview_move_forward_character (GtkTextView *text)
9716 {
9717         GtkTextBuffer *buffer;
9718         GtkTextMark *mark;
9719         GtkTextIter ins;
9720
9721         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9722
9723         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9724         mark = gtk_text_buffer_get_insert(buffer);
9725         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9726         if (gtk_text_iter_forward_cursor_position(&ins))
9727                 gtk_text_buffer_place_cursor(buffer, &ins);
9728 }
9729
9730 static void textview_move_backward_character (GtkTextView *text)
9731 {
9732         GtkTextBuffer *buffer;
9733         GtkTextMark *mark;
9734         GtkTextIter ins;
9735
9736         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9737
9738         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9739         mark = gtk_text_buffer_get_insert(buffer);
9740         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9741         if (gtk_text_iter_backward_cursor_position(&ins))
9742                 gtk_text_buffer_place_cursor(buffer, &ins);
9743 }
9744
9745 static void textview_move_forward_word (GtkTextView *text)
9746 {
9747         GtkTextBuffer *buffer;
9748         GtkTextMark *mark;
9749         GtkTextIter ins;
9750         gint count;
9751
9752         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9753
9754         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9755         mark = gtk_text_buffer_get_insert(buffer);
9756         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9757         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9758         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9759                 gtk_text_iter_backward_word_start(&ins);
9760                 gtk_text_buffer_place_cursor(buffer, &ins);
9761         }
9762 }
9763
9764 static void textview_move_backward_word (GtkTextView *text)
9765 {
9766         GtkTextBuffer *buffer;
9767         GtkTextMark *mark;
9768         GtkTextIter ins;
9769         gint count;
9770
9771         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9772
9773         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9774         mark = gtk_text_buffer_get_insert(buffer);
9775         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9776         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9777         if (gtk_text_iter_backward_word_starts(&ins, 1))
9778                 gtk_text_buffer_place_cursor(buffer, &ins);
9779 }
9780
9781 static void textview_move_end_of_line (GtkTextView *text)
9782 {
9783         GtkTextBuffer *buffer;
9784         GtkTextMark *mark;
9785         GtkTextIter ins;
9786
9787         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9788
9789         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9790         mark = gtk_text_buffer_get_insert(buffer);
9791         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9792         if (gtk_text_iter_forward_to_line_end(&ins))
9793                 gtk_text_buffer_place_cursor(buffer, &ins);
9794 }
9795
9796 static void textview_move_next_line (GtkTextView *text)
9797 {
9798         GtkTextBuffer *buffer;
9799         GtkTextMark *mark;
9800         GtkTextIter ins;
9801         gint offset;
9802
9803         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9804
9805         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9806         mark = gtk_text_buffer_get_insert(buffer);
9807         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9808         offset = gtk_text_iter_get_line_offset(&ins);
9809         if (gtk_text_iter_forward_line(&ins)) {
9810                 gtk_text_iter_set_line_offset(&ins, offset);
9811                 gtk_text_buffer_place_cursor(buffer, &ins);
9812         }
9813 }
9814
9815 static void textview_move_previous_line (GtkTextView *text)
9816 {
9817         GtkTextBuffer *buffer;
9818         GtkTextMark *mark;
9819         GtkTextIter ins;
9820         gint offset;
9821
9822         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9823
9824         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9825         mark = gtk_text_buffer_get_insert(buffer);
9826         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9827         offset = gtk_text_iter_get_line_offset(&ins);
9828         if (gtk_text_iter_backward_line(&ins)) {
9829                 gtk_text_iter_set_line_offset(&ins, offset);
9830                 gtk_text_buffer_place_cursor(buffer, &ins);
9831         }
9832 }
9833
9834 static void textview_delete_forward_character (GtkTextView *text)
9835 {
9836         GtkTextBuffer *buffer;
9837         GtkTextMark *mark;
9838         GtkTextIter ins, end_iter;
9839
9840         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9841
9842         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9843         mark = gtk_text_buffer_get_insert(buffer);
9844         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9845         end_iter = ins;
9846         if (gtk_text_iter_forward_char(&end_iter)) {
9847                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9848         }
9849 }
9850
9851 static void textview_delete_backward_character (GtkTextView *text)
9852 {
9853         GtkTextBuffer *buffer;
9854         GtkTextMark *mark;
9855         GtkTextIter ins, end_iter;
9856
9857         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9858
9859         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9860         mark = gtk_text_buffer_get_insert(buffer);
9861         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9862         end_iter = ins;
9863         if (gtk_text_iter_backward_char(&end_iter)) {
9864                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9865         }
9866 }
9867
9868 static void textview_delete_forward_word (GtkTextView *text)
9869 {
9870         GtkTextBuffer *buffer;
9871         GtkTextMark *mark;
9872         GtkTextIter ins, end_iter;
9873
9874         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9875
9876         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9877         mark = gtk_text_buffer_get_insert(buffer);
9878         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9879         end_iter = ins;
9880         if (gtk_text_iter_forward_word_end(&end_iter)) {
9881                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9882         }
9883 }
9884
9885 static void textview_delete_backward_word (GtkTextView *text)
9886 {
9887         GtkTextBuffer *buffer;
9888         GtkTextMark *mark;
9889         GtkTextIter ins, end_iter;
9890
9891         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9892
9893         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9894         mark = gtk_text_buffer_get_insert(buffer);
9895         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9896         end_iter = ins;
9897         if (gtk_text_iter_backward_word_start(&end_iter)) {
9898                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9899         }
9900 }
9901
9902 static void textview_delete_line (GtkTextView *text)
9903 {
9904         GtkTextBuffer *buffer;
9905         GtkTextMark *mark;
9906         GtkTextIter ins, start_iter, end_iter;
9907
9908         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9909
9910         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9911         mark = gtk_text_buffer_get_insert(buffer);
9912         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9913
9914         start_iter = ins;
9915         gtk_text_iter_set_line_offset(&start_iter, 0);
9916
9917         end_iter = ins;
9918         if (gtk_text_iter_ends_line(&end_iter)){
9919                 if (!gtk_text_iter_forward_char(&end_iter))
9920                         gtk_text_iter_backward_char(&start_iter);
9921         }
9922         else 
9923                 gtk_text_iter_forward_to_line_end(&end_iter);
9924         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9925 }
9926
9927 static void textview_delete_to_line_end (GtkTextView *text)
9928 {
9929         GtkTextBuffer *buffer;
9930         GtkTextMark *mark;
9931         GtkTextIter ins, end_iter;
9932
9933         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9934
9935         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9936         mark = gtk_text_buffer_get_insert(buffer);
9937         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9938         end_iter = ins;
9939         if (gtk_text_iter_ends_line(&end_iter))
9940                 gtk_text_iter_forward_char(&end_iter);
9941         else
9942                 gtk_text_iter_forward_to_line_end(&end_iter);
9943         gtk_text_buffer_delete(buffer, &ins, &end_iter);
9944 }
9945
9946 #define DO_ACTION(name, act) {                                          \
9947         if(!strcmp(name, a_name)) {                                     \
9948                 return act;                                             \
9949         }                                                               \
9950 }
9951 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9952 {
9953         const gchar *a_name = gtk_action_get_name(action);
9954         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9955         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9956         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9957         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9958         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9959         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9960         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9961         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9962         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9963         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9964         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9965         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9966         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9967         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9968         return -1;
9969 }
9970
9971 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9972 {
9973         Compose *compose = (Compose *)data;
9974         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9975         ComposeCallAdvancedAction action = -1;
9976         
9977         action = compose_call_advanced_action_from_path(gaction);
9978
9979         static struct {
9980                 void (*do_action) (GtkTextView *text);
9981         } action_table[] = {
9982                 {textview_move_beginning_of_line},
9983                 {textview_move_forward_character},
9984                 {textview_move_backward_character},
9985                 {textview_move_forward_word},
9986                 {textview_move_backward_word},
9987                 {textview_move_end_of_line},
9988                 {textview_move_next_line},
9989                 {textview_move_previous_line},
9990                 {textview_delete_forward_character},
9991                 {textview_delete_backward_character},
9992                 {textview_delete_forward_word},
9993                 {textview_delete_backward_word},
9994                 {textview_delete_line},
9995                 {textview_delete_to_line_end}
9996         };
9997
9998         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9999
10000         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10001             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10002                 if (action_table[action].do_action)
10003                         action_table[action].do_action(text);
10004                 else
10005                         g_warning("Not implemented yet.");
10006         }
10007 }
10008
10009 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10010 {
10011         gchar *str = NULL;
10012         
10013         if (GTK_IS_EDITABLE(widget)) {
10014                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10015                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10016                         strlen(str));
10017                 g_free(str);
10018                 if (widget->parent && widget->parent->parent
10019                  && widget->parent->parent->parent) {
10020                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10021                                 gint y = widget->allocation.y;
10022                                 gint height = widget->allocation.height;
10023                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10024                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10025
10026                                 if (y < (int)shown->value) {
10027                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10028                                 }
10029                                 if (y + height > (int)shown->value + (int)shown->page_size) {
10030                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10031                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10032                                                         y + height - (int)shown->page_size - 1);
10033                                         } else {
10034                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10035                                                         (int)shown->upper - (int)shown->page_size - 1);
10036                                         }
10037                                 }
10038                         }
10039                 }
10040         }
10041
10042         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10043                 compose->focused_editable = widget;
10044         
10045 #ifdef GENERIC_UMPC
10046         if (GTK_IS_TEXT_VIEW(widget) 
10047             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10048                 g_object_ref(compose->notebook);
10049                 g_object_ref(compose->edit_vbox);
10050                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10051                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10052                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10053                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10054                 g_object_unref(compose->notebook);
10055                 g_object_unref(compose->edit_vbox);
10056                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10057                                         G_CALLBACK(compose_grab_focus_cb),
10058                                         compose);
10059                 gtk_widget_grab_focus(widget);
10060                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10061                                         G_CALLBACK(compose_grab_focus_cb),
10062                                         compose);
10063         } else if (!GTK_IS_TEXT_VIEW(widget) 
10064                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10065                 g_object_ref(compose->notebook);
10066                 g_object_ref(compose->edit_vbox);
10067                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10068                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10069                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10070                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10071                 g_object_unref(compose->notebook);
10072                 g_object_unref(compose->edit_vbox);
10073                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10074                                         G_CALLBACK(compose_grab_focus_cb),
10075                                         compose);
10076                 gtk_widget_grab_focus(widget);
10077                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10078                                         G_CALLBACK(compose_grab_focus_cb),
10079                                         compose);
10080         }
10081 #endif
10082 }
10083
10084 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10085 {
10086         compose->modified = TRUE;
10087 //      compose_beautify_paragraph(compose, NULL, TRUE);
10088 #ifndef GENERIC_UMPC
10089         compose_set_title(compose);
10090 #endif
10091 }
10092
10093 static void compose_wrap_cb(GtkAction *action, gpointer data)
10094 {
10095         Compose *compose = (Compose *)data;
10096         compose_beautify_paragraph(compose, NULL, TRUE);
10097 }
10098
10099 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10100 {
10101         Compose *compose = (Compose *)data;
10102         compose_wrap_all_full(compose, TRUE);
10103 }
10104
10105 static void compose_find_cb(GtkAction *action, gpointer data)
10106 {
10107         Compose *compose = (Compose *)data;
10108
10109         message_search_compose(compose);
10110 }
10111
10112 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10113                                          gpointer        data)
10114 {
10115         Compose *compose = (Compose *)data;
10116         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10117         if (compose->autowrap)
10118                 compose_wrap_all_full(compose, TRUE);
10119         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10120 }
10121
10122 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10123                                          gpointer        data)
10124 {
10125         Compose *compose = (Compose *)data;
10126         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10127 }
10128
10129 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10130 {
10131         Compose *compose = (Compose *)data;
10132
10133         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10134 }
10135
10136 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10137 {
10138         Compose *compose = (Compose *)data;
10139
10140         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10141 }
10142
10143 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10144 {
10145         g_free(compose->privacy_system);
10146
10147         compose->privacy_system = g_strdup(account->default_privacy_system);
10148         compose_update_privacy_system_menu_item(compose, warn);
10149 }
10150
10151 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10152 {
10153         Compose *compose = (Compose *)data;
10154
10155         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10156                 gtk_widget_show(compose->ruler_hbox);
10157                 prefs_common.show_ruler = TRUE;
10158         } else {
10159                 gtk_widget_hide(compose->ruler_hbox);
10160                 gtk_widget_queue_resize(compose->edit_vbox);
10161                 prefs_common.show_ruler = FALSE;
10162         }
10163 }
10164
10165 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10166                                              GdkDragContext     *context,
10167                                              gint                x,
10168                                              gint                y,
10169                                              GtkSelectionData   *data,
10170                                              guint               info,
10171                                              guint               time,
10172                                              gpointer            user_data)
10173 {
10174         Compose *compose = (Compose *)user_data;
10175         GList *list, *tmp;
10176
10177         if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10178 #ifdef G_OS_WIN32
10179          || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10180 #endif
10181            ) && gtk_drag_get_source_widget(context) != 
10182                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10183                 list = uri_list_extract_filenames((const gchar *)data->data);
10184                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10185                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10186                         compose_attach_append
10187                                 (compose, (const gchar *)tmp->data,
10188                                  utf8_filename, NULL);
10189                         g_free(utf8_filename);
10190                 }
10191                 if (list) compose_changed_cb(NULL, compose);
10192                 list_free_strings(list);
10193                 g_list_free(list);
10194         } else if (gtk_drag_get_source_widget(context) 
10195                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10196                 /* comes from our summaryview */
10197                 SummaryView * summaryview = NULL;
10198                 GSList * list = NULL, *cur = NULL;
10199                 
10200                 if (mainwindow_get_mainwindow())
10201                         summaryview = mainwindow_get_mainwindow()->summaryview;
10202                 
10203                 if (summaryview)
10204                         list = summary_get_selected_msg_list(summaryview);
10205                 
10206                 for (cur = list; cur; cur = cur->next) {
10207                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10208                         gchar *file = NULL;
10209                         if (msginfo)
10210                                 file = procmsg_get_message_file_full(msginfo, 
10211                                         TRUE, TRUE);
10212                         if (file) {
10213                                 compose_attach_append(compose, (const gchar *)file, 
10214                                         (const gchar *)file, "message/rfc822");
10215                                 g_free(file);
10216                         }
10217                 }
10218                 g_slist_free(list);
10219         }
10220 }
10221
10222 static gboolean compose_drag_drop(GtkWidget *widget,
10223                                   GdkDragContext *drag_context,
10224                                   gint x, gint y,
10225                                   guint time, gpointer user_data)
10226 {
10227         /* not handling this signal makes compose_insert_drag_received_cb
10228          * called twice */
10229         return TRUE;                                     
10230 }
10231
10232 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10233                                              GdkDragContext     *drag_context,
10234                                              gint                x,
10235                                              gint                y,
10236                                              GtkSelectionData   *data,
10237                                              guint               info,
10238                                              guint               time,
10239                                              gpointer            user_data)
10240 {
10241         Compose *compose = (Compose *)user_data;
10242         GList *list, *tmp;
10243
10244         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10245          * does not work */
10246 #ifndef G_OS_WIN32
10247         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10248 #else
10249         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10250 #endif
10251                 AlertValue val = G_ALERTDEFAULT;
10252
10253                 list = uri_list_extract_filenames((const gchar *)data->data);
10254                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10255                         /* Assume a list of no files, and data has ://, is a remote link */
10256                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10257                         gchar *tmpfile = get_tmp_file();
10258                         str_write_to_file(tmpdata, tmpfile);
10259                         g_free(tmpdata);  
10260                         compose_insert_file(compose, tmpfile);
10261                         claws_unlink(tmpfile);
10262                         g_free(tmpfile);
10263                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10264                         compose_beautify_paragraph(compose, NULL, TRUE);
10265                         return;
10266                 }
10267                 switch (prefs_common.compose_dnd_mode) {
10268                         case COMPOSE_DND_ASK:
10269                                 val = alertpanel_full(_("Insert or attach?"),
10270                                          _("Do you want to insert the contents of the file(s) "
10271                                            "into the message body, or attach it to the email?"),
10272                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10273                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10274                                 break;
10275                         case COMPOSE_DND_INSERT:
10276                                 val = G_ALERTALTERNATE;
10277                                 break;
10278                         case COMPOSE_DND_ATTACH:
10279                                 val = G_ALERTOTHER;
10280                                 break;
10281                         default:
10282                                 /* unexpected case */
10283                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10284                 }
10285
10286                 if (val & G_ALERTDISABLE) {
10287                         val &= ~G_ALERTDISABLE;
10288                         /* remember what action to perform by default, only if we don't click Cancel */
10289                         if (val == G_ALERTALTERNATE)
10290                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10291                         else if (val == G_ALERTOTHER)
10292                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10293                 }
10294
10295                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10296                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10297                         list_free_strings(list);
10298                         g_list_free(list);
10299                         return;
10300                 } else if (val == G_ALERTOTHER) {
10301                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10302                         list_free_strings(list);
10303                         g_list_free(list);
10304                         return;
10305                 } 
10306
10307                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10308                         compose_insert_file(compose, (const gchar *)tmp->data);
10309                 }
10310                 list_free_strings(list);
10311                 g_list_free(list);
10312                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10313                 return;
10314         } else {
10315 #if GTK_CHECK_VERSION(2, 8, 0)
10316                 /* do nothing, handled by GTK */
10317 #else
10318                 gchar *tmpfile = get_tmp_file();
10319                 str_write_to_file((const gchar *)data->data, tmpfile);
10320                 compose_insert_file(compose, tmpfile);
10321                 claws_unlink(tmpfile);
10322                 g_free(tmpfile);
10323                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10324 #endif
10325                 return;
10326         }
10327         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10328 }
10329
10330 static void compose_header_drag_received_cb (GtkWidget          *widget,
10331                                              GdkDragContext     *drag_context,
10332                                              gint                x,
10333                                              gint                y,
10334                                              GtkSelectionData   *data,
10335                                              guint               info,
10336                                              guint               time,
10337                                              gpointer            user_data)
10338 {
10339         GtkEditable *entry = (GtkEditable *)user_data;
10340         gchar *email = (gchar *)data->data;
10341
10342         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10343          * does not work */
10344
10345         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10346                 gchar *decoded=g_new(gchar, strlen(email));
10347                 int start = 0;
10348
10349                 email += strlen("mailto:");
10350                 decode_uri(decoded, email); /* will fit */
10351                 gtk_editable_delete_text(entry, 0, -1);
10352                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10353                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10354                 g_free(decoded);
10355                 return;
10356         }
10357         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10358 }
10359
10360 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10361 {
10362         Compose *compose = (Compose *)data;
10363
10364         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10365                 compose->return_receipt = TRUE;
10366         else
10367                 compose->return_receipt = FALSE;
10368 }
10369
10370 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10371 {
10372         Compose *compose = (Compose *)data;
10373
10374         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10375                 compose->remove_references = TRUE;
10376         else
10377                 compose->remove_references = FALSE;
10378 }
10379
10380 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10381                                             GdkEventKey *event,
10382                                             ComposeHeaderEntry *headerentry)
10383 {
10384         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10385             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10386             !(event->state & GDK_MODIFIER_MASK) &&
10387             (event->keyval == GDK_BackSpace) &&
10388             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10389                 gtk_container_remove
10390                         (GTK_CONTAINER(headerentry->compose->header_table),
10391                          headerentry->combo);
10392                 gtk_container_remove
10393                         (GTK_CONTAINER(headerentry->compose->header_table),
10394                          headerentry->entry);
10395                 headerentry->compose->header_list =
10396                         g_slist_remove(headerentry->compose->header_list,
10397                                        headerentry);
10398                 g_free(headerentry);
10399         } else  if (event->keyval == GDK_Tab) {
10400                 if (headerentry->compose->header_last == headerentry) {
10401                         /* Override default next focus, and give it to subject_entry
10402                          * instead of notebook tabs
10403                          */
10404                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10405                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10406                         return TRUE;
10407                 }
10408         }
10409         return FALSE;
10410 }
10411
10412 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10413                                     ComposeHeaderEntry *headerentry)
10414 {
10415         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10416                 compose_create_header_entry(headerentry->compose);
10417                 g_signal_handlers_disconnect_matched
10418                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10419                          0, 0, NULL, NULL, headerentry);
10420                 
10421                 /* Automatically scroll down */
10422                 compose_show_first_last_header(headerentry->compose, FALSE);
10423                 
10424         }
10425         return FALSE;
10426 }
10427
10428 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10429 {
10430         GtkAdjustment *vadj;
10431
10432         cm_return_if_fail(compose);
10433         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10434         cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10435
10436         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10437         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10438         gtk_adjustment_changed(vadj);
10439 }
10440
10441 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10442                           const gchar *text, gint len, Compose *compose)
10443 {
10444         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10445                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10446         GtkTextMark *mark;
10447
10448         cm_return_if_fail(text != NULL);
10449
10450         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10451                                         G_CALLBACK(text_inserted),
10452                                         compose);
10453         if (paste_as_quotation) {
10454                 gchar *new_text;
10455                 const gchar *qmark;
10456                 guint pos = 0;
10457                 GtkTextIter start_iter;
10458
10459                 if (len < 0)
10460                         len = strlen(text);
10461
10462                 new_text = g_strndup(text, len);
10463
10464                 qmark = compose_quote_char_from_context(compose);
10465
10466                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10467                 gtk_text_buffer_place_cursor(buffer, iter);
10468
10469                 pos = gtk_text_iter_get_offset(iter);
10470
10471                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10472                                                   _("Quote format error at line %d."));
10473                 quote_fmt_reset_vartable();
10474                 g_free(new_text);
10475                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10476                                   GINT_TO_POINTER(paste_as_quotation - 1));
10477                                   
10478                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10479                 gtk_text_buffer_place_cursor(buffer, iter);
10480                 gtk_text_buffer_delete_mark(buffer, mark);
10481
10482                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10483                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10484                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10485                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10486                 gtk_text_buffer_delete_mark(buffer, mark);
10487         } else {
10488                 if (strcmp(text, "\n") || compose->automatic_break
10489                 || gtk_text_iter_starts_line(iter)) {
10490                         GtkTextIter before_ins;
10491                         gtk_text_buffer_insert(buffer, iter, text, len);
10492                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10493                                 before_ins = *iter; 
10494                                 gtk_text_iter_backward_chars(&before_ins, len);
10495                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10496                         }
10497                 } else {
10498                         /* check if the preceding is just whitespace or quote */
10499                         GtkTextIter start_line;
10500                         gchar *tmp = NULL, *quote = NULL;
10501                         gint quote_len = 0, is_normal = 0;
10502                         start_line = *iter;
10503                         gtk_text_iter_set_line_offset(&start_line, 0); 
10504                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10505                         g_strstrip(tmp);
10506
10507                         if (*tmp == '\0') {
10508                                 is_normal = 1;
10509                         } else {
10510                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10511                                 if (quote)
10512                                         is_normal = 1;
10513                                 g_free(quote);
10514                         }
10515                         g_free(tmp);
10516                         
10517                         if (is_normal) {
10518                                 gtk_text_buffer_insert(buffer, iter, text, len);
10519                         } else {
10520                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10521                                         iter, text, len, "no_join", NULL);
10522                         }
10523                 }
10524         }
10525         
10526         if (!paste_as_quotation) {
10527                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10528                 compose_beautify_paragraph(compose, iter, FALSE);
10529                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10530                 gtk_text_buffer_delete_mark(buffer, mark);
10531         }
10532
10533         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10534                                           G_CALLBACK(text_inserted),
10535                                           compose);
10536         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10537
10538         if (prefs_common.autosave && 
10539             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10540             compose->draft_timeout_tag != -2 /* disabled while loading */)
10541                 compose->draft_timeout_tag = g_timeout_add
10542                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10543 }
10544 static gint compose_defer_auto_save_draft(Compose *compose)
10545 {
10546         compose->draft_timeout_tag = -1;
10547         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10548         return FALSE;
10549 }
10550
10551 #if USE_ENCHANT
10552 static void compose_check_all(GtkAction *action, gpointer data)
10553 {
10554         Compose *compose = (Compose *)data;
10555         if (!compose->gtkaspell)
10556                 return;
10557                 
10558         if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10559                 claws_spell_entry_check_all(
10560                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
10561         else
10562                 gtkaspell_check_all(compose->gtkaspell);
10563 }
10564
10565 static void compose_highlight_all(GtkAction *action, gpointer data)
10566 {
10567         Compose *compose = (Compose *)data;
10568         if (compose->gtkaspell) {
10569                 claws_spell_entry_recheck_all(
10570                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10571                 gtkaspell_highlight_all(compose->gtkaspell);
10572         }
10573 }
10574
10575 static void compose_check_backwards(GtkAction *action, gpointer data)
10576 {
10577         Compose *compose = (Compose *)data;
10578         if (!compose->gtkaspell) {
10579                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10580                 return;
10581         }
10582
10583         if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10584                 claws_spell_entry_check_backwards(
10585                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10586         else
10587                 gtkaspell_check_backwards(compose->gtkaspell);
10588 }
10589
10590 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10591 {
10592         Compose *compose = (Compose *)data;
10593         if (!compose->gtkaspell) {
10594                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10595                 return;
10596         }
10597
10598         if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10599                 claws_spell_entry_check_forwards_go(
10600                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10601         else
10602                 gtkaspell_check_forwards_go(compose->gtkaspell);
10603 }
10604 #endif
10605
10606 /*!
10607  *\brief        Guess originating forward account from MsgInfo and several 
10608  *              "common preference" settings. Return NULL if no guess. 
10609  */
10610 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10611 {
10612         PrefsAccount *account = NULL;
10613         
10614         cm_return_val_if_fail(msginfo, NULL);
10615         cm_return_val_if_fail(msginfo->folder, NULL);
10616         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10617
10618         if (msginfo->folder->prefs->enable_default_account)
10619                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10620                 
10621         if (!account) 
10622                 account = msginfo->folder->folder->account;
10623                 
10624         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10625                 gchar *to;
10626                 Xstrdup_a(to, msginfo->to, return NULL);
10627                 extract_address(to);
10628                 account = account_find_from_address(to, FALSE);
10629         }
10630
10631         if (!account && prefs_common.forward_account_autosel) {
10632                 gchar cc[BUFFSIZE];
10633                 if (!procheader_get_header_from_msginfo
10634                         (msginfo, cc,sizeof cc , "Cc:")) { 
10635                         gchar *buf = cc + strlen("Cc:");
10636                         extract_address(buf);
10637                         account = account_find_from_address(buf, FALSE);
10638                 }
10639         }
10640         
10641         if (!account && prefs_common.forward_account_autosel) {
10642                 gchar deliveredto[BUFFSIZE];
10643                 if (!procheader_get_header_from_msginfo
10644                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10645                         gchar *buf = deliveredto + strlen("Delivered-To:");
10646                         extract_address(buf);
10647                         account = account_find_from_address(buf, FALSE);
10648                 }
10649         }
10650         
10651         return account;
10652 }
10653
10654 gboolean compose_close(Compose *compose)
10655 {
10656         gint x, y;
10657
10658         if (!g_mutex_trylock(compose->mutex)) {
10659                 /* we have to wait for the (possibly deferred by auto-save)
10660                  * drafting to be done, before destroying the compose under
10661                  * it. */
10662                 debug_print("waiting for drafting to finish...\n");
10663                 compose_allow_user_actions(compose, FALSE);
10664                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10665                 return FALSE;
10666         }
10667         cm_return_val_if_fail(compose, FALSE);
10668         gtkut_widget_get_uposition(compose->window, &x, &y);
10669         if (!compose->batch) {
10670                 prefs_common.compose_x = x;
10671                 prefs_common.compose_y = y;
10672         }
10673         g_mutex_unlock(compose->mutex);
10674         compose_destroy(compose);
10675         return FALSE;
10676 }
10677
10678 /**
10679  * Add entry field for each address in list.
10680  * \param compose     E-Mail composition object.
10681  * \param listAddress List of (formatted) E-Mail addresses.
10682  */
10683 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10684         GList *node;
10685         gchar *addr;
10686         node = listAddress;
10687         while( node ) {
10688                 addr = ( gchar * ) node->data;
10689                 compose_entry_append( compose, addr, COMPOSE_TO );
10690                 node = g_list_next( node );
10691         }
10692 }
10693
10694 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10695                                     guint action, gboolean opening_multiple)
10696 {
10697         gchar *body = NULL;
10698         GSList *new_msglist = NULL;
10699         MsgInfo *tmp_msginfo = NULL;
10700         gboolean originally_enc = FALSE;
10701         gboolean originally_sig = FALSE;
10702         Compose *compose = NULL;
10703         gchar *s_system = NULL;
10704
10705         cm_return_if_fail(msgview != NULL);
10706
10707         cm_return_if_fail(msginfo_list != NULL);
10708
10709         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10710                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10711                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10712
10713                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10714                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10715                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10716                                                 orig_msginfo, mimeinfo);
10717                         if (tmp_msginfo != NULL) {
10718                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10719
10720                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10721                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10722                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10723
10724                                 tmp_msginfo->folder = orig_msginfo->folder;
10725                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10726                                 if (orig_msginfo->tags) {
10727                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10728                                         tmp_msginfo->folder->tags_dirty = TRUE;
10729                                 }
10730                         }
10731                 }
10732         }
10733
10734         if (!opening_multiple)
10735                 body = messageview_get_selection(msgview);
10736
10737         if (new_msglist) {
10738                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10739                 procmsg_msginfo_free(tmp_msginfo);
10740                 g_slist_free(new_msglist);
10741         } else
10742                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10743
10744         if (compose && originally_enc) {
10745                 compose_force_encryption(compose, compose->account, FALSE, s_system);
10746         }
10747
10748         if (compose && originally_sig && compose->account->default_sign_reply) {
10749                 compose_force_signing(compose, compose->account, s_system);
10750         }
10751         g_free(s_system);
10752         g_free(body);
10753 }
10754
10755 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10756                                     guint action)
10757 {
10758         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10759         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10760                 GSList *cur = msginfo_list;
10761                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10762                                                "messages. Opening the windows "
10763                                                "could take some time. Do you "
10764                                                "want to continue?"), 
10765                                                g_slist_length(msginfo_list));
10766                 if (g_slist_length(msginfo_list) > 9
10767                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10768                     != G_ALERTALTERNATE) {
10769                         g_free(msg);
10770                         return;
10771                 }
10772                 g_free(msg);
10773                 /* We'll open multiple compose windows */
10774                 /* let the WM place the next windows */
10775                 compose_force_window_origin = FALSE;
10776                 for (; cur; cur = cur->next) {
10777                         GSList tmplist;
10778                         tmplist.data = cur->data;
10779                         tmplist.next = NULL;
10780                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10781                 }
10782                 compose_force_window_origin = TRUE;
10783         } else {
10784                 /* forwarding multiple mails as attachments is done via a
10785                  * single compose window */
10786                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10787         }
10788 }
10789
10790 void compose_set_position(Compose *compose, gint pos)
10791 {
10792         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10793
10794         gtkut_text_view_set_position(text, pos);
10795 }
10796
10797 gboolean compose_search_string(Compose *compose,
10798                                 const gchar *str, gboolean case_sens)
10799 {
10800         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10801
10802         return gtkut_text_view_search_string(text, str, case_sens);
10803 }
10804
10805 gboolean compose_search_string_backward(Compose *compose,
10806                                 const gchar *str, gboolean case_sens)
10807 {
10808         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10809
10810         return gtkut_text_view_search_string_backward(text, str, case_sens);
10811 }
10812
10813 /* allocate a msginfo structure and populate its data from a compose data structure */
10814 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10815 {
10816         MsgInfo *newmsginfo;
10817         GSList *list;
10818         gchar buf[BUFFSIZE];
10819
10820         cm_return_val_if_fail( compose != NULL, NULL );
10821
10822         newmsginfo = procmsg_msginfo_new();
10823
10824         /* date is now */
10825         get_rfc822_date(buf, sizeof(buf));
10826         newmsginfo->date = g_strdup(buf);
10827
10828         /* from */
10829         if (compose->from_name) {
10830                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10831                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10832         }
10833
10834         /* subject */
10835         if (compose->subject_entry)
10836                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10837
10838         /* to, cc, reply-to, newsgroups */
10839         for (list = compose->header_list; list; list = list->next) {
10840                 gchar *header = gtk_editable_get_chars(
10841                                                                 GTK_EDITABLE(
10842                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10843                 gchar *entry = gtk_editable_get_chars(
10844                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10845
10846                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10847                         if ( newmsginfo->to == NULL ) {
10848                                 newmsginfo->to = g_strdup(entry);
10849                         } else if (entry && *entry) {
10850                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10851                                 g_free(newmsginfo->to);
10852                                 newmsginfo->to = tmp;
10853                         }
10854                 } else
10855                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10856                         if ( newmsginfo->cc == NULL ) {
10857                                 newmsginfo->cc = g_strdup(entry);
10858                         } else if (entry && *entry) {
10859                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10860                                 g_free(newmsginfo->cc);
10861                                 newmsginfo->cc = tmp;
10862                         }
10863                 } else
10864                 if ( strcasecmp(header,
10865                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10866                         if ( newmsginfo->newsgroups == NULL ) {
10867                                 newmsginfo->newsgroups = g_strdup(entry);
10868                         } else if (entry && *entry) {
10869                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10870                                 g_free(newmsginfo->newsgroups);
10871                                 newmsginfo->newsgroups = tmp;
10872                         }
10873                 }
10874
10875                 g_free(header);
10876                 g_free(entry);  
10877         }
10878
10879         /* other data is unset */
10880
10881         return newmsginfo;
10882 }
10883
10884 #ifdef USE_ENCHANT
10885 /* update compose's dictionaries from folder dict settings */
10886 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10887                                                 FolderItem *folder_item)
10888 {
10889         cm_return_if_fail(compose != NULL);
10890
10891         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10892                 FolderItemPrefs *prefs = folder_item->prefs;
10893
10894                 if (prefs->enable_default_dictionary)
10895                         gtkaspell_change_dict(compose->gtkaspell,
10896                                         prefs->default_dictionary, FALSE);
10897                 if (folder_item->prefs->enable_default_alt_dictionary)
10898                         gtkaspell_change_alt_dict(compose->gtkaspell,
10899                                         prefs->default_alt_dictionary);
10900                 if (prefs->enable_default_dictionary
10901                         || prefs->enable_default_alt_dictionary)
10902                         compose_spell_menu_changed(compose);
10903         }
10904 }
10905 #endif
10906
10907 /*
10908  * End of Source.
10909  */