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