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