2009-05-21 [mones] 3.7.1cvs61
[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_BUTTON));
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                 g_free(headerentry);
6452         }
6453         compose->header_last = NULL;
6454         g_slist_free(compose->header_list);
6455         compose->header_list = NULL;
6456         compose->header_nextrow = 1;
6457         compose_create_header_entry(compose);
6458 }
6459
6460 static GtkWidget *compose_create_header(Compose *compose) 
6461 {
6462         GtkWidget *from_optmenu_hbox;
6463         GtkWidget *header_scrolledwin;
6464         GtkWidget *header_table;
6465
6466         gint count = 0;
6467
6468         /* header labels and entries */
6469         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6470         gtk_widget_show(header_scrolledwin);
6471         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6472
6473         header_table = gtk_table_new(2, 2, FALSE);
6474         gtk_widget_show(header_table);
6475         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6476         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6477         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6478         count = 0;
6479
6480         /* option menu for selecting accounts */
6481         from_optmenu_hbox = compose_account_option_menu_create(compose);
6482         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6483                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6484         count++;
6485
6486         compose->header_table = header_table;
6487         compose->header_list = NULL;
6488         compose->header_nextrow = count;
6489
6490         compose_create_header_entry(compose);
6491
6492         compose->table            = NULL;
6493
6494         return header_scrolledwin ;
6495 }
6496
6497 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6498 {
6499         Compose *compose = (Compose *)data;
6500         GdkEventButton event;
6501         
6502         event.button = 3;
6503         event.time = gtk_get_current_event_time();
6504
6505         return attach_button_pressed(compose->attach_clist, &event, compose);
6506 }
6507
6508 static GtkWidget *compose_create_attach(Compose *compose)
6509 {
6510         GtkWidget *attach_scrwin;
6511         GtkWidget *attach_clist;
6512
6513         GtkListStore *store;
6514         GtkCellRenderer *renderer;
6515         GtkTreeViewColumn *column;
6516         GtkTreeSelection *selection;
6517
6518         /* attachment list */
6519         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6520         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6521                                        GTK_POLICY_AUTOMATIC,
6522                                        GTK_POLICY_AUTOMATIC);
6523         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6524
6525         store = gtk_list_store_new(N_ATTACH_COLS, 
6526                                    G_TYPE_STRING,
6527                                    G_TYPE_STRING,
6528                                    G_TYPE_STRING,
6529                                    G_TYPE_POINTER,
6530                                    G_TYPE_AUTO_POINTER,
6531                                    -1);
6532         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6533                                         (GTK_TREE_MODEL(store)));
6534         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6535         g_object_unref(store);
6536         
6537         renderer = gtk_cell_renderer_text_new();
6538         column = gtk_tree_view_column_new_with_attributes
6539                         (_("Mime type"), renderer, "text", 
6540                          COL_MIMETYPE, NULL);
6541         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6542         
6543         renderer = gtk_cell_renderer_text_new();
6544         column = gtk_tree_view_column_new_with_attributes
6545                         (_("Size"), renderer, "text", 
6546                          COL_SIZE, NULL);
6547         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6548         
6549         renderer = gtk_cell_renderer_text_new();
6550         column = gtk_tree_view_column_new_with_attributes
6551                         (_("Name"), renderer, "text", 
6552                          COL_NAME, NULL);
6553         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6554
6555         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6556                                      prefs_common.use_stripes_everywhere);
6557         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6558         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6559
6560         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6561                          G_CALLBACK(attach_selected), compose);
6562         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6563                          G_CALLBACK(attach_button_pressed), compose);
6564 #ifndef MAEMO
6565         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6566                          G_CALLBACK(popup_attach_button_pressed), compose);
6567 #else
6568         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6569                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6570         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6571                          G_CALLBACK(popup_attach_button_pressed), compose);
6572 #endif
6573         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6574                          G_CALLBACK(attach_key_pressed), compose);
6575
6576         /* drag and drop */
6577         gtk_drag_dest_set(attach_clist,
6578                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6579                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6580                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6581         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6582                          G_CALLBACK(compose_attach_drag_received_cb),
6583                          compose);
6584         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6585                          G_CALLBACK(compose_drag_drop),
6586                          compose);
6587
6588         compose->attach_scrwin = attach_scrwin;
6589         compose->attach_clist  = attach_clist;
6590
6591         return attach_scrwin;
6592 }
6593
6594 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6595 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6596
6597 static GtkWidget *compose_create_others(Compose *compose)
6598 {
6599         GtkWidget *table;
6600         GtkWidget *savemsg_checkbtn;
6601         GtkWidget *savemsg_combo;
6602         GtkWidget *savemsg_select;
6603         
6604         guint rowcount = 0;
6605         gchar *folderidentifier;
6606
6607         /* Table for settings */
6608         table = gtk_table_new(3, 1, FALSE);
6609         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6610         gtk_widget_show(table);
6611         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6612         rowcount = 0;
6613
6614         /* Save Message to folder */
6615         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6616         gtk_widget_show(savemsg_checkbtn);
6617         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6618         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6619                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6620         }
6621         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6622                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6623
6624         savemsg_combo = gtk_combo_box_entry_new_text();
6625         compose->savemsg_checkbtn = savemsg_checkbtn;
6626         compose->savemsg_combo = savemsg_combo;
6627         gtk_widget_show(savemsg_combo);
6628
6629         if (prefs_common.compose_save_to_history)
6630                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6631                                 prefs_common.compose_save_to_history);
6632
6633         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6634         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6635         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6636                          G_CALLBACK(compose_grab_focus_cb), compose);
6637         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6638                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6639                                   (compose->account, F_OUTBOX));
6640                 compose_set_save_to(compose, folderidentifier);
6641                 g_free(folderidentifier);
6642         }
6643
6644         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6645         gtk_widget_show(savemsg_select);
6646         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6647         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6648                          G_CALLBACK(compose_savemsg_select_cb),
6649                          compose);
6650
6651         rowcount++;
6652
6653         return table;   
6654 }
6655
6656 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6657 {
6658         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6659                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6660 }
6661
6662 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6663 {
6664         FolderItem *dest;
6665         gchar * path;
6666
6667         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6668         if (!dest) return;
6669
6670         path = folder_item_get_identifier(dest);
6671
6672         compose_set_save_to(compose, path);
6673         g_free(path);
6674 }
6675
6676 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6677                                   GdkAtom clip, GtkTextIter *insert_place);
6678
6679
6680 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6681                                        Compose *compose)
6682 {
6683         gint prev_autowrap;
6684         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6685 #if USE_ENCHANT
6686         if (event->button == 3) {
6687                 GtkTextIter iter;
6688                 GtkTextIter sel_start, sel_end;
6689                 gboolean stuff_selected;
6690                 gint x, y;
6691                 /* move the cursor to allow GtkAspell to check the word
6692                  * under the mouse */
6693                 if (event->x && event->y) {
6694                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6695                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6696                                 &x, &y);
6697                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6698                                 &iter, x, y);
6699                 } else {
6700                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6701                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6702                 }
6703                 /* get selection */
6704                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6705                                 buffer,
6706                                 &sel_start, &sel_end);
6707
6708                 gtk_text_buffer_place_cursor (buffer, &iter);
6709                 /* reselect stuff */
6710                 if (stuff_selected 
6711                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6712                         gtk_text_buffer_select_range(buffer,
6713                                 &sel_start, &sel_end);
6714                 }
6715                 return FALSE; /* pass the event so that the right-click goes through */
6716         }
6717 #endif
6718         if (event->button == 2) {
6719                 GtkTextIter iter;
6720                 gint x, y;
6721                 BLOCK_WRAP();
6722                 
6723                 /* get the middle-click position to paste at the correct place */
6724                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6725                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6726                         &x, &y);
6727                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6728                         &iter, x, y);
6729                 
6730                 entry_paste_clipboard(compose, text, 
6731                                 prefs_common.linewrap_pastes,
6732                                 GDK_SELECTION_PRIMARY, &iter);
6733                 UNBLOCK_WRAP();
6734                 return TRUE;
6735         }
6736         return FALSE;
6737 }
6738
6739 #if USE_ENCHANT
6740 static void compose_spell_menu_changed(void *data)
6741 {
6742         Compose *compose = (Compose *)data;
6743         GSList *items;
6744         GtkWidget *menuitem;
6745         GtkWidget *parent_item;
6746         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6747         GSList *spell_menu;
6748
6749         if (compose->gtkaspell == NULL)
6750                 return;
6751
6752         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
6753                         "/Menu/Spelling/Options");
6754
6755         /* setting the submenu removes /Spelling/Options from the factory 
6756          * so we need to save it */
6757
6758         if (parent_item == NULL) {
6759                 parent_item = compose->aspell_options_menu;
6760                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6761         } else
6762                 compose->aspell_options_menu = parent_item;
6763
6764         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6765
6766         spell_menu = g_slist_reverse(spell_menu);
6767         for (items = spell_menu;
6768              items; items = items->next) {
6769                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6770                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6771                 gtk_widget_show(GTK_WIDGET(menuitem));
6772         }
6773         g_slist_free(spell_menu);
6774
6775         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6776         gtk_widget_show(parent_item);
6777 }
6778
6779 static void compose_dict_changed(void *data)
6780 {
6781         Compose *compose = (Compose *) data;
6782
6783         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
6784                 return;
6785
6786         gtkaspell_highlight_all(compose->gtkaspell);
6787         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6788 }
6789 #endif
6790
6791 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6792 {
6793         Compose *compose = (Compose *)data;
6794         GdkEventButton event;
6795         
6796         event.button = 3;
6797         event.time = gtk_get_current_event_time();
6798         event.x = 0;
6799         event.y = 0;
6800
6801         return text_clicked(compose->text, &event, compose);
6802 }
6803
6804 static gboolean compose_force_window_origin = TRUE;
6805 static Compose *compose_create(PrefsAccount *account,
6806                                                  FolderItem *folder,
6807                                                  ComposeMode mode,
6808                                                  gboolean batch)
6809 {
6810         Compose   *compose;
6811         GtkWidget *window;
6812         GtkWidget *vbox;
6813         GtkWidget *menubar;
6814         GtkWidget *handlebox;
6815
6816         GtkWidget *notebook;
6817         
6818         GtkWidget *attach_hbox;
6819         GtkWidget *attach_lab1;
6820         GtkWidget *attach_lab2;
6821
6822         GtkWidget *vbox2;
6823
6824         GtkWidget *label;
6825         GtkWidget *subject_hbox;
6826         GtkWidget *subject_frame;
6827         GtkWidget *subject_entry;
6828         GtkWidget *subject;
6829         GtkWidget *paned;
6830
6831         GtkWidget *edit_vbox;
6832         GtkWidget *ruler_hbox;
6833         GtkWidget *ruler;
6834         GtkWidget *scrolledwin;
6835         GtkWidget *text;
6836         GtkTextBuffer *buffer;
6837         GtkClipboard *clipboard;
6838         CLAWS_TIP_DECL();
6839
6840         UndoMain *undostruct;
6841
6842         gchar *titles[N_ATTACH_COLS];
6843         GtkWidget *popupmenu;
6844         GtkWidget *tmpl_menu;
6845         GtkActionGroup *action_group = NULL;
6846
6847 #if USE_ENCHANT
6848         GtkAspell * gtkaspell = NULL;
6849 #endif
6850
6851         static GdkGeometry geometry;
6852
6853         cm_return_val_if_fail(account != NULL, NULL);
6854
6855         debug_print("Creating compose window...\n");
6856         compose = g_new0(Compose, 1);
6857
6858         titles[COL_MIMETYPE] = _("MIME type");
6859         titles[COL_SIZE]     = _("Size");
6860         titles[COL_NAME]     = _("Name");
6861
6862         compose->batch = batch;
6863         compose->account = account;
6864         compose->folder = folder;
6865         
6866         compose->mutex = g_mutex_new();
6867         compose->set_cursor_pos = -1;
6868
6869 #if !(GTK_CHECK_VERSION(2,12,0))
6870         compose->tooltips = tips;
6871 #endif
6872
6873         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6874
6875         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6876         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6877
6878         if (!geometry.max_width) {
6879                 geometry.max_width = gdk_screen_width();
6880                 geometry.max_height = gdk_screen_height();
6881         }
6882
6883         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6884                                       &geometry, GDK_HINT_MAX_SIZE);
6885         if (!geometry.min_width) {
6886                 geometry.min_width = 600;
6887                 geometry.min_height = 440;
6888         }
6889         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6890                                       &geometry, GDK_HINT_MIN_SIZE);
6891
6892 #ifndef GENERIC_UMPC    
6893         if (compose_force_window_origin)
6894                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
6895                                  prefs_common.compose_y);
6896 #endif
6897         g_signal_connect(G_OBJECT(window), "delete_event",
6898                          G_CALLBACK(compose_delete_cb), compose);
6899         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6900         gtk_widget_realize(window);
6901
6902         gtkut_widget_set_composer_icon(window);
6903
6904         vbox = gtk_vbox_new(FALSE, 0);
6905         gtk_container_add(GTK_CONTAINER(window), vbox);
6906
6907         compose->ui_manager = gtk_ui_manager_new();
6908         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6909                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
6910         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6911                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6912         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6913                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6914         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6915                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6916         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6917                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6918
6919 #ifndef MAEMO
6920         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6921 #else
6922         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6923 #endif
6924
6925         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6926         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6927 #ifdef USE_ENCHANT
6928         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6929 #endif
6930         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6931         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6932         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6933
6934 /* Compose menu */
6935         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6936         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6937         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6938         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6939         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6940         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6941         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6942         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6943         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6944         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6945
6946 /* Edit menu */
6947         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6948         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6949         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6950
6951         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6952         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6953         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6954
6955         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6956         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6957         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6958         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6959
6960         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6961
6962         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6963         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6964         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6965         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6966         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6967         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6968         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6969         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6970         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6971         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6972         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6973         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6974         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6975         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6976         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6977
6978         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6979
6980         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6981         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6982         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6983         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6984         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6985
6986         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6987
6988         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6989
6990 #if USE_ENCHANT
6991 /* Spelling menu */
6992         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6993         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6994         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6995         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6996         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6997         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6998 #endif
6999
7000 /* Options menu */
7001         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7002         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7003         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7004         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7005         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7006
7007         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7008         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7009         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7010         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7011         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7012
7013         
7014         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7015         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7016         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7017         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7018         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7019         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7020         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7021
7022         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7023         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7024         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7025         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7026         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7027
7028         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7029
7030         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7031         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7032         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7033         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7034         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7035
7036         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7037         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)
7038         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)
7039         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7040
7041         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7042
7043         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7044         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)
7045         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)
7046
7047         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7048
7049         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7050         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)
7051         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7052
7053         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7054         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)
7055         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7056
7057         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7058
7059         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7060         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)
7061         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7062         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7063         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7064
7065         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7066         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)
7067         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)
7068         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7069         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7070
7071         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7072         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7073         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7074         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7075         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7076
7077         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7078         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7079         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)
7080
7081         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7082         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7083         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7084 /* phew. */
7085
7086 /* Tools menu */
7087         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7088         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7089         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7090         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7091         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7092         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7093
7094 /* Help menu */
7095         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7096
7097         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7098         gtk_widget_show_all(menubar);
7099
7100         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7101 #ifndef MAEMO
7102         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7103 #else
7104         hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7105 #endif
7106
7107         if (prefs_common.toolbar_detachable) {
7108                 handlebox = gtk_handle_box_new();
7109         } else {
7110                 handlebox = gtk_hbox_new(FALSE, 0);
7111         }
7112         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7113
7114         gtk_widget_realize(handlebox);
7115 #ifdef MAEMO
7116         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7117                                           (gpointer)compose);
7118 #else
7119         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7120                                           (gpointer)compose);
7121 #endif
7122
7123         vbox2 = gtk_vbox_new(FALSE, 2);
7124         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7125         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7126         
7127         /* Notebook */
7128         notebook = gtk_notebook_new();
7129         gtk_widget_set_size_request(notebook, -1, 130);
7130         gtk_widget_show(notebook);
7131
7132         /* header labels and entries */
7133         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7134                         compose_create_header(compose),
7135                         gtk_label_new_with_mnemonic(_("Hea_der")));
7136         /* attachment list */
7137         attach_hbox = gtk_hbox_new(FALSE, 0);
7138         gtk_widget_show(attach_hbox);
7139         
7140         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7141         gtk_widget_show(attach_lab1);
7142         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7143         
7144         attach_lab2 = gtk_label_new("");
7145         gtk_widget_show(attach_lab2);
7146         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7147         
7148         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7149                         compose_create_attach(compose),
7150                         attach_hbox);
7151         /* Others Tab */
7152         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7153                         compose_create_others(compose),
7154                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7155
7156         /* Subject */
7157         subject_hbox = gtk_hbox_new(FALSE, 0);
7158         gtk_widget_show(subject_hbox);
7159
7160         subject_frame = gtk_frame_new(NULL);
7161         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7162         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7163         gtk_widget_show(subject_frame);
7164
7165         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7166         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7167         gtk_widget_show(subject);
7168
7169         label = gtk_label_new(_("Subject:"));
7170         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7171         gtk_widget_show(label);
7172
7173 #ifdef USE_ENCHANT
7174         subject_entry = claws_spell_entry_new();
7175 #else
7176         subject_entry = gtk_entry_new();
7177 #endif
7178         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7179         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7180                          G_CALLBACK(compose_grab_focus_cb), compose);
7181         gtk_widget_show(subject_entry);
7182         compose->subject_entry = subject_entry;
7183         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7184         
7185         edit_vbox = gtk_vbox_new(FALSE, 0);
7186
7187         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7188
7189         /* ruler */
7190         ruler_hbox = gtk_hbox_new(FALSE, 0);
7191         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7192
7193         ruler = gtk_shruler_new();
7194         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7195         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7196                            BORDER_WIDTH);
7197
7198         /* text widget */
7199         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7200         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7201                                        GTK_POLICY_AUTOMATIC,
7202                                        GTK_POLICY_AUTOMATIC);
7203         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7204                                             GTK_SHADOW_IN);
7205         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7206         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7207
7208         text = gtk_text_view_new();
7209         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7210         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7211         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7212         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7213         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7214         
7215         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7216
7217         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7218                                G_CALLBACK(compose_edit_size_alloc),
7219                                ruler);
7220         g_signal_connect(G_OBJECT(buffer), "changed",
7221                          G_CALLBACK(compose_changed_cb), compose);
7222         g_signal_connect(G_OBJECT(text), "grab_focus",
7223                          G_CALLBACK(compose_grab_focus_cb), compose);
7224         g_signal_connect(G_OBJECT(buffer), "insert_text",
7225                          G_CALLBACK(text_inserted), compose);
7226         g_signal_connect(G_OBJECT(text), "button_press_event",
7227                          G_CALLBACK(text_clicked), compose);
7228 #ifndef MAEMO
7229         g_signal_connect(G_OBJECT(text), "popup-menu",
7230                          G_CALLBACK(compose_popup_menu), compose);
7231 #else
7232         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7233                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7234         g_signal_connect(G_OBJECT(text), "tap-and-hold",
7235                          G_CALLBACK(compose_popup_menu), compose);
7236 #endif
7237         g_signal_connect(G_OBJECT(subject_entry), "changed",
7238                          G_CALLBACK(compose_changed_cb), compose);
7239
7240         /* drag and drop */
7241         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7242                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7243                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7244         g_signal_connect(G_OBJECT(text), "drag_data_received",
7245                          G_CALLBACK(compose_insert_drag_received_cb),
7246                          compose);
7247         g_signal_connect(G_OBJECT(text), "drag-drop",
7248                          G_CALLBACK(compose_drag_drop),
7249                          compose);
7250         gtk_widget_show_all(vbox);
7251
7252         /* pane between attach clist and text */
7253         paned = gtk_vpaned_new();
7254         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7255 #ifdef MAEMO
7256         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7257                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7258         else
7259                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7260 #endif
7261         gtk_paned_add1(GTK_PANED(paned), notebook);
7262         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7263         gtk_widget_show_all(paned);
7264
7265
7266         if (prefs_common.textfont) {
7267                 PangoFontDescription *font_desc;
7268
7269                 font_desc = pango_font_description_from_string
7270                         (prefs_common.textfont);
7271                 if (font_desc) {
7272                         gtk_widget_modify_font(text, font_desc);
7273                         pango_font_description_free(font_desc);
7274                 }
7275         }
7276
7277         gtk_action_group_add_actions(action_group, compose_popup_entries,
7278                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7279         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7280         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7281         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7282         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7283         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7284         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7285         
7286         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7287
7288         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7289         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7290         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7291
7292         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7293
7294         undostruct = undo_init(text);
7295         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7296                                    compose);
7297
7298         address_completion_start(window);
7299
7300         compose->window        = window;
7301         compose->vbox          = vbox;
7302         compose->menubar       = menubar;
7303         compose->handlebox     = handlebox;
7304
7305         compose->vbox2         = vbox2;
7306
7307         compose->paned = paned;
7308
7309         compose->attach_label  = attach_lab2;
7310
7311         compose->notebook      = notebook;
7312         compose->edit_vbox     = edit_vbox;
7313         compose->ruler_hbox    = ruler_hbox;
7314         compose->ruler         = ruler;
7315         compose->scrolledwin   = scrolledwin;
7316         compose->text          = text;
7317
7318         compose->focused_editable = NULL;
7319
7320         compose->popupmenu    = popupmenu;
7321
7322         compose->tmpl_menu = tmpl_menu;
7323
7324         compose->mode = mode;
7325         compose->rmode = mode;
7326
7327         compose->targetinfo = NULL;
7328         compose->replyinfo  = NULL;
7329         compose->fwdinfo    = NULL;
7330
7331         compose->replyto     = NULL;
7332         compose->cc          = NULL;
7333         compose->bcc         = NULL;
7334         compose->followup_to = NULL;
7335
7336         compose->ml_post     = NULL;
7337
7338         compose->inreplyto   = NULL;
7339         compose->references  = NULL;
7340         compose->msgid       = NULL;
7341         compose->boundary    = NULL;
7342
7343         compose->autowrap       = prefs_common.autowrap;
7344         compose->autoindent     = prefs_common.auto_indent;
7345         compose->use_signing    = FALSE;
7346         compose->use_encryption = FALSE;
7347         compose->privacy_system = NULL;
7348
7349         compose->modified = FALSE;
7350
7351         compose->return_receipt = FALSE;
7352
7353         compose->to_list        = NULL;
7354         compose->newsgroup_list = NULL;
7355
7356         compose->undostruct = undostruct;
7357
7358         compose->sig_str = NULL;
7359
7360         compose->exteditor_file    = NULL;
7361         compose->exteditor_pid     = -1;
7362         compose->exteditor_tag     = -1;
7363         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7364
7365 #if USE_ENCHANT
7366         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7367         if (mode != COMPOSE_REDIRECT) {
7368                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7369                     strcmp(prefs_common.dictionary, "")) {
7370                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7371                                                   prefs_common.alt_dictionary,
7372                                                   conv_get_locale_charset_str(),
7373                                                   prefs_common.misspelled_col,
7374                                                   prefs_common.check_while_typing,
7375                                                   prefs_common.recheck_when_changing_dict,
7376                                                   prefs_common.use_alternate,
7377                                                   prefs_common.use_both_dicts,
7378                                                   GTK_TEXT_VIEW(text),
7379                                                   GTK_WINDOW(compose->window),
7380                                                   compose_dict_changed,
7381                                                   compose_spell_menu_changed,
7382                                                   compose);
7383                         if (!gtkaspell) {
7384                                 alertpanel_error(_("Spell checker could not "
7385                                                 "be started.\n%s"),
7386                                                 gtkaspell_checkers_strerror());
7387                                 gtkaspell_checkers_reset_error();
7388                         } else {
7389                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7390                         }
7391                 }
7392         }
7393         compose->gtkaspell = gtkaspell;
7394         compose_spell_menu_changed(compose);
7395         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7396 #endif
7397
7398         compose_select_account(compose, account, TRUE);
7399
7400         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7401         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7402
7403         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7404                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7405
7406         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7407                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7408         
7409         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7410                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7411
7412         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7413         if (account->protocol != A_NNTP)
7414                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7415                                 prefs_common_translated_header_name("To:"));
7416         else
7417                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7418                                 prefs_common_translated_header_name("Newsgroups:"));
7419
7420         addressbook_set_target_compose(compose);
7421         
7422         if (mode != COMPOSE_REDIRECT)
7423                 compose_set_template_menu(compose);
7424         else {
7425                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7426         }
7427
7428         compose_list = g_list_append(compose_list, compose);
7429
7430         if (!prefs_common.show_ruler)
7431                 gtk_widget_hide(ruler_hbox);
7432                 
7433         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7434
7435         /* Priority */
7436         compose->priority = PRIORITY_NORMAL;
7437         compose_update_priority_menu_item(compose);
7438
7439         compose_set_out_encoding(compose);
7440         
7441         /* Actions menu */
7442         compose_update_actions_menu(compose);
7443
7444         /* Privacy Systems menu */
7445         compose_update_privacy_systems_menu(compose);
7446
7447         activate_privacy_system(compose, account, TRUE);
7448         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7449         if (batch) {
7450                 gtk_widget_realize(window);
7451         } else {
7452                 gtk_widget_show(window);
7453 #ifdef MAEMO
7454                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7455                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7456 #endif
7457         }
7458         
7459         return compose;
7460 }
7461
7462 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7463 {
7464         GList *accounts;
7465         GtkWidget *hbox;
7466         GtkWidget *optmenu;
7467         GtkWidget *optmenubox;
7468         GtkListStore *menu;
7469         GtkTreeIter iter;
7470         GtkWidget *from_name = NULL;
7471 #if !(GTK_CHECK_VERSION(2,12,0))
7472         GtkTooltips *tips = compose->tooltips;
7473 #endif
7474
7475         gint num = 0, def_menu = 0;
7476         
7477         accounts = account_get_list();
7478         cm_return_val_if_fail(accounts != NULL, NULL);
7479
7480         optmenubox = gtk_event_box_new();
7481         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7482         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7483
7484         hbox = gtk_hbox_new(FALSE, 6);
7485         from_name = gtk_entry_new();
7486         
7487         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7488                          G_CALLBACK(compose_grab_focus_cb), compose);
7489
7490         for (; accounts != NULL; accounts = accounts->next, num++) {
7491                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7492                 gchar *name, *from = NULL;
7493
7494                 if (ac == compose->account) def_menu = num;
7495
7496                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7497                                        ac->account_name);
7498                 
7499                 if (ac == compose->account) {
7500                         if (ac->name && *ac->name) {
7501                                 gchar *buf;
7502                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7503                                 from = g_strdup_printf("%s <%s>",
7504                                                        buf, ac->address);
7505                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7506                         } else {
7507                                 from = g_strdup_printf("%s",
7508                                                        ac->address);
7509                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7510                         }
7511                 }
7512                 COMBOBOX_ADD(menu, name, ac->account_id);
7513                 g_free(name);
7514                 g_free(from);
7515         }
7516
7517         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7518
7519         g_signal_connect(G_OBJECT(optmenu), "changed",
7520                         G_CALLBACK(account_activated),
7521                         compose);
7522         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7523                          G_CALLBACK(compose_entry_popup_extend),
7524                          NULL);
7525
7526         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7527         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7528         
7529         CLAWS_SET_TIP(optmenubox,
7530                 _("Account to use for this email"));
7531         CLAWS_SET_TIP(from_name,
7532                 _("Sender address to be used"));
7533
7534         compose->from_name = from_name;
7535         
7536         return hbox;
7537 }
7538
7539 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7540 {
7541         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7542         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7543         Compose *compose = (Compose *) data;
7544         if (active) {
7545                 compose->priority = value;
7546         }
7547 }
7548
7549 static void compose_reply_change_mode(Compose *compose,
7550                                     ComposeMode action)
7551 {
7552         gboolean was_modified = compose->modified;
7553
7554         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7555         
7556         cm_return_if_fail(compose->replyinfo != NULL);
7557         
7558         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7559                 ml = TRUE;
7560         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7561                 followup = TRUE;
7562         if (action == COMPOSE_REPLY_TO_ALL)
7563                 all = TRUE;
7564         if (action == COMPOSE_REPLY_TO_SENDER)
7565                 sender = TRUE;
7566         if (action == COMPOSE_REPLY_TO_LIST)
7567                 ml = TRUE;
7568
7569         compose_remove_header_entries(compose);
7570         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7571         if (compose->account->set_autocc && compose->account->auto_cc)
7572                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7573
7574         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7575                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7576         
7577         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7578                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7579         compose_show_first_last_header(compose, TRUE);
7580         compose->modified = was_modified;
7581         compose_set_title(compose);
7582 }
7583
7584 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7585 {
7586         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7587         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7588         Compose *compose = (Compose *) data;
7589         
7590         if (active)
7591                 compose_reply_change_mode(compose, value);
7592 }
7593
7594 static void compose_update_priority_menu_item(Compose * compose)
7595 {
7596         GtkWidget *menuitem = NULL;
7597         switch (compose->priority) {
7598                 case PRIORITY_HIGHEST:
7599                         menuitem = gtk_ui_manager_get_widget
7600                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7601                         break;
7602                 case PRIORITY_HIGH:
7603                         menuitem = gtk_ui_manager_get_widget
7604                                 (compose->ui_manager, "/Menu/Options/Priority/High");
7605                         break;
7606                 case PRIORITY_NORMAL:
7607                         menuitem = gtk_ui_manager_get_widget
7608                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7609                         break;
7610                 case PRIORITY_LOW:
7611                         menuitem = gtk_ui_manager_get_widget
7612                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
7613                         break;
7614                 case PRIORITY_LOWEST:
7615                         menuitem = gtk_ui_manager_get_widget
7616                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7617                         break;
7618         }
7619         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7620 }       
7621
7622 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7623 {
7624         Compose *compose = (Compose *) data;
7625         gchar *systemid;
7626         gboolean can_sign = FALSE, can_encrypt = FALSE;
7627
7628         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7629
7630         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7631                 return;
7632
7633         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7634         g_free(compose->privacy_system);
7635         compose->privacy_system = NULL;
7636         if (systemid != NULL) {
7637                 compose->privacy_system = g_strdup(systemid);
7638
7639                 can_sign = privacy_system_can_sign(systemid);
7640                 can_encrypt = privacy_system_can_encrypt(systemid);
7641         }
7642
7643         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7644
7645         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7646         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7647 }
7648
7649 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7650 {
7651         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7652         GtkWidget *menuitem = NULL;
7653         GList *amenu;
7654         gboolean can_sign = FALSE, can_encrypt = FALSE;
7655         gboolean found = FALSE;
7656
7657         if (compose->privacy_system != NULL) {
7658                 gchar *systemid;
7659                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7660                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7661                 cm_return_if_fail(menuitem != NULL);
7662
7663                 amenu = GTK_MENU_SHELL(menuitem)->children;
7664                 menuitem = NULL;
7665                 while (amenu != NULL) {
7666                         GList *alist = amenu->next;
7667
7668                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7669                         if (systemid != NULL) {
7670                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
7671                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7672                                         menuitem = GTK_WIDGET(amenu->data);
7673
7674                                         can_sign = privacy_system_can_sign(systemid);
7675                                         can_encrypt = privacy_system_can_encrypt(systemid);
7676                                         found = TRUE;
7677                                         break;
7678                                 } 
7679                         } else if (strlen(compose->privacy_system) == 0 && 
7680                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7681                                         menuitem = GTK_WIDGET(amenu->data);
7682
7683                                         can_sign = FALSE;
7684                                         can_encrypt = FALSE;
7685                                         found = TRUE;
7686                                         break;
7687                         }
7688
7689                         amenu = alist;
7690                 }
7691                 if (menuitem != NULL)
7692                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7693                 
7694                 if (warn && !found && strlen(compose->privacy_system)) {
7695                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7696                                   "will not be able to sign or encrypt this message."),
7697                                   compose->privacy_system);
7698                 }
7699         } 
7700
7701         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7702         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7703 }       
7704  
7705 static void compose_set_out_encoding(Compose *compose)
7706 {
7707         CharSet out_encoding;
7708         const gchar *branch = NULL;
7709         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7710
7711         switch(out_encoding) {
7712                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7713                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7714                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7715                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7716                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7717                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7718                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7719                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7720                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7721                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7722                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7723                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7724                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7725                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7726                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7727                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7728                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7729                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7730                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7731                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7732                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7733                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7734                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7735                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7736                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7737                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7738                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7739                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7740                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7741                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7742                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7743                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7744         }
7745         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7746 }
7747
7748 static void compose_set_template_menu(Compose *compose)
7749 {
7750         GSList *tmpl_list, *cur;
7751         GtkWidget *menu;
7752         GtkWidget *item;
7753
7754         tmpl_list = template_get_config();
7755
7756         menu = gtk_menu_new();
7757
7758         gtk_menu_set_accel_group (GTK_MENU (menu), 
7759                 gtk_ui_manager_get_accel_group(compose->ui_manager));
7760         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7761                 Template *tmpl = (Template *)cur->data;
7762                 gchar *accel_path = NULL;
7763                 item = gtk_menu_item_new_with_label(tmpl->name);
7764                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7765                 g_signal_connect(G_OBJECT(item), "activate",
7766                                  G_CALLBACK(compose_template_activate_cb),
7767                                  compose);
7768                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7769                 gtk_widget_show(item);
7770                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7771                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7772                 g_free(accel_path);
7773         }
7774
7775         gtk_widget_show(menu);
7776         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7777 }
7778
7779 void compose_update_actions_menu(Compose *compose)
7780 {
7781         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7782 }
7783
7784 static void compose_update_privacy_systems_menu(Compose *compose)
7785 {
7786         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7787         GSList *systems, *cur;
7788         GtkWidget *widget;
7789         GtkWidget *system_none;
7790         GSList *group;
7791         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7792         GtkWidget *privacy_menu = gtk_menu_new();
7793
7794         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7795         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7796
7797         g_signal_connect(G_OBJECT(system_none), "activate",
7798                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7799
7800         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7801         gtk_widget_show(system_none);
7802
7803         systems = privacy_get_system_ids();
7804         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7805                 gchar *systemid = cur->data;
7806
7807                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7808                 widget = gtk_radio_menu_item_new_with_label(group,
7809                         privacy_system_get_name(systemid));
7810                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7811                                        g_strdup(systemid), g_free);
7812                 g_signal_connect(G_OBJECT(widget), "activate",
7813                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7814
7815                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7816                 gtk_widget_show(widget);
7817                 g_free(systemid);
7818         }
7819         g_slist_free(systems);
7820         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7821         gtk_widget_show_all(privacy_menu);
7822         gtk_widget_show_all(privacy_menuitem);
7823 }
7824
7825 void compose_reflect_prefs_all(void)
7826 {
7827         GList *cur;
7828         Compose *compose;
7829
7830         for (cur = compose_list; cur != NULL; cur = cur->next) {
7831                 compose = (Compose *)cur->data;
7832                 compose_set_template_menu(compose);
7833         }
7834 }
7835
7836 void compose_reflect_prefs_pixmap_theme(void)
7837 {
7838         GList *cur;
7839         Compose *compose;
7840
7841         for (cur = compose_list; cur != NULL; cur = cur->next) {
7842                 compose = (Compose *)cur->data;
7843                 toolbar_update(TOOLBAR_COMPOSE, compose);
7844         }
7845 }
7846
7847 static const gchar *compose_quote_char_from_context(Compose *compose)
7848 {
7849         const gchar *qmark = NULL;
7850
7851         cm_return_val_if_fail(compose != NULL, NULL);
7852
7853         switch (compose->mode) {
7854                 /* use forward-specific quote char */
7855                 case COMPOSE_FORWARD:
7856                 case COMPOSE_FORWARD_AS_ATTACH:
7857                 case COMPOSE_FORWARD_INLINE:
7858                         if (compose->folder && compose->folder->prefs &&
7859                                         compose->folder->prefs->forward_with_format)
7860                                 qmark = compose->folder->prefs->forward_quotemark;
7861                         else if (compose->account->forward_with_format)
7862                                 qmark = compose->account->forward_quotemark;
7863                         else
7864                                 qmark = prefs_common.fw_quotemark;
7865                         break;
7866
7867                 /* use reply-specific quote char in all other modes */
7868                 default:
7869                         if (compose->folder && compose->folder->prefs &&
7870                                         compose->folder->prefs->reply_with_format)
7871                                 qmark = compose->folder->prefs->reply_quotemark;
7872                         else if (compose->account->reply_with_format)
7873                                 qmark = compose->account->reply_quotemark;
7874                         else
7875                                 qmark = prefs_common.quotemark;
7876                         break;
7877         }
7878
7879         if (qmark == NULL || *qmark == '\0')
7880                 qmark = "> ";
7881
7882         return qmark;
7883 }
7884
7885 static void compose_template_apply(Compose *compose, Template *tmpl,
7886                                    gboolean replace)
7887 {
7888         GtkTextView *text;
7889         GtkTextBuffer *buffer;
7890         GtkTextMark *mark;
7891         GtkTextIter iter;
7892         const gchar *qmark;
7893         gchar *parsed_str = NULL;
7894         gint cursor_pos = 0;
7895         const gchar *err_msg = _("Template body format error at line %d.");
7896         if (!tmpl) return;
7897
7898         /* process the body */
7899
7900         text = GTK_TEXT_VIEW(compose->text);
7901         buffer = gtk_text_view_get_buffer(text);
7902
7903         if (tmpl->value) {
7904                 qmark = compose_quote_char_from_context(compose);
7905
7906                 if (compose->replyinfo != NULL) {
7907
7908                         if (replace)
7909                                 gtk_text_buffer_set_text(buffer, "", -1);
7910                         mark = gtk_text_buffer_get_insert(buffer);
7911                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7912
7913                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7914                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7915
7916                 } else if (compose->fwdinfo != NULL) {
7917
7918                         if (replace)
7919                                 gtk_text_buffer_set_text(buffer, "", -1);
7920                         mark = gtk_text_buffer_get_insert(buffer);
7921                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7922
7923                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7924                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7925
7926                 } else {
7927                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7928
7929                         GtkTextIter start, end;
7930                         gchar *tmp = NULL;
7931
7932                         gtk_text_buffer_get_start_iter(buffer, &start);
7933                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7934                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7935
7936                         /* clear the buffer now */
7937                         if (replace)
7938                                 gtk_text_buffer_set_text(buffer, "", -1);
7939
7940                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7941                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7942                         procmsg_msginfo_free( dummyinfo );
7943
7944                         g_free( tmp );
7945                 } 
7946         } else {
7947                 if (replace)
7948                         gtk_text_buffer_set_text(buffer, "", -1);
7949                 mark = gtk_text_buffer_get_insert(buffer);
7950                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7951         }       
7952
7953         if (replace && parsed_str && compose->account->auto_sig)
7954                 compose_insert_sig(compose, FALSE);
7955
7956         if (replace && parsed_str) {
7957                 gtk_text_buffer_get_start_iter(buffer, &iter);
7958                 gtk_text_buffer_place_cursor(buffer, &iter);
7959         }
7960         
7961         if (parsed_str) {
7962                 cursor_pos = quote_fmt_get_cursor_pos();
7963                 compose->set_cursor_pos = cursor_pos;
7964                 if (cursor_pos == -1)
7965                         cursor_pos = 0;
7966                 gtk_text_buffer_get_start_iter(buffer, &iter);
7967                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7968                 gtk_text_buffer_place_cursor(buffer, &iter);
7969         }
7970
7971         /* process the other fields */
7972
7973         compose_template_apply_fields(compose, tmpl);
7974         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7975         quote_fmt_reset_vartable();
7976         compose_changed_cb(NULL, compose);
7977 }
7978
7979 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7980 {
7981         MsgInfo* dummyinfo = NULL;
7982         MsgInfo *msginfo = NULL;
7983         gchar *buf = NULL;
7984
7985         if (compose->replyinfo != NULL)
7986                 msginfo = compose->replyinfo;
7987         else if (compose->fwdinfo != NULL)
7988                 msginfo = compose->fwdinfo;
7989         else {
7990                 dummyinfo = compose_msginfo_new_from_compose(compose);
7991                 msginfo = dummyinfo;
7992         }
7993
7994         if (tmpl->from && *tmpl->from != '\0') {
7995 #ifdef USE_ENCHANT
7996                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7997                                 compose->gtkaspell);
7998 #else
7999                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8000 #endif
8001                 quote_fmt_scan_string(tmpl->from);
8002                 quote_fmt_parse();
8003
8004                 buf = quote_fmt_get_buffer();
8005                 if (buf == NULL) {
8006                         alertpanel_error(_("Template From format error."));
8007                 } else {
8008                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8009                 }
8010         }
8011
8012         if (tmpl->to && *tmpl->to != '\0') {
8013 #ifdef USE_ENCHANT
8014                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8015                                 compose->gtkaspell);
8016 #else
8017                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8018 #endif
8019                 quote_fmt_scan_string(tmpl->to);
8020                 quote_fmt_parse();
8021
8022                 buf = quote_fmt_get_buffer();
8023                 if (buf == NULL) {
8024                         alertpanel_error(_("Template To format error."));
8025                 } else {
8026                         compose_entry_append(compose, buf, COMPOSE_TO);
8027                 }
8028         }
8029
8030         if (tmpl->cc && *tmpl->cc != '\0') {
8031 #ifdef USE_ENCHANT
8032                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8033                                 compose->gtkaspell);
8034 #else
8035                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8036 #endif
8037                 quote_fmt_scan_string(tmpl->cc);
8038                 quote_fmt_parse();
8039
8040                 buf = quote_fmt_get_buffer();
8041                 if (buf == NULL) {
8042                         alertpanel_error(_("Template Cc format error."));
8043                 } else {
8044                         compose_entry_append(compose, buf, COMPOSE_CC);
8045                 }
8046         }
8047
8048         if (tmpl->bcc && *tmpl->bcc != '\0') {
8049 #ifdef USE_ENCHANT
8050                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8051                                 compose->gtkaspell);
8052 #else
8053                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8054 #endif
8055                 quote_fmt_scan_string(tmpl->bcc);
8056                 quote_fmt_parse();
8057
8058                 buf = quote_fmt_get_buffer();
8059                 if (buf == NULL) {
8060                         alertpanel_error(_("Template Bcc format error."));
8061                 } else {
8062                         compose_entry_append(compose, buf, COMPOSE_BCC);
8063                 }
8064         }
8065
8066         /* process the subject */
8067         if (tmpl->subject && *tmpl->subject != '\0') {
8068 #ifdef USE_ENCHANT
8069                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8070                                 compose->gtkaspell);
8071 #else
8072                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8073 #endif
8074                 quote_fmt_scan_string(tmpl->subject);
8075                 quote_fmt_parse();
8076
8077                 buf = quote_fmt_get_buffer();
8078                 if (buf == NULL) {
8079                         alertpanel_error(_("Template subject format error."));
8080                 } else {
8081                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8082                 }
8083         }
8084
8085         procmsg_msginfo_free( dummyinfo );
8086 }
8087
8088 static void compose_destroy(Compose *compose)
8089 {
8090         GtkTextBuffer *buffer;
8091         GtkClipboard *clipboard;
8092
8093         compose_list = g_list_remove(compose_list, compose);
8094
8095         if (compose->updating) {
8096                 debug_print("danger, not destroying anything now\n");
8097                 compose->deferred_destroy = TRUE;
8098                 return;
8099         }
8100         /* NOTE: address_completion_end() does nothing with the window
8101          * however this may change. */
8102         address_completion_end(compose->window);
8103
8104         slist_free_strings(compose->to_list);
8105         g_slist_free(compose->to_list);
8106         slist_free_strings(compose->newsgroup_list);
8107         g_slist_free(compose->newsgroup_list);
8108         slist_free_strings(compose->header_list);
8109         g_slist_free(compose->header_list);
8110
8111         procmsg_msginfo_free(compose->targetinfo);
8112         procmsg_msginfo_free(compose->replyinfo);
8113         procmsg_msginfo_free(compose->fwdinfo);
8114
8115         g_free(compose->replyto);
8116         g_free(compose->cc);
8117         g_free(compose->bcc);
8118         g_free(compose->newsgroups);
8119         g_free(compose->followup_to);
8120
8121         g_free(compose->ml_post);
8122
8123         g_free(compose->inreplyto);
8124         g_free(compose->references);
8125         g_free(compose->msgid);
8126         g_free(compose->boundary);
8127
8128         g_free(compose->redirect_filename);
8129         if (compose->undostruct)
8130                 undo_destroy(compose->undostruct);
8131
8132         g_free(compose->sig_str);
8133
8134         g_free(compose->exteditor_file);
8135
8136         g_free(compose->orig_charset);
8137
8138         g_free(compose->privacy_system);
8139
8140         if (addressbook_get_target_compose() == compose)
8141                 addressbook_set_target_compose(NULL);
8142
8143 #if USE_ENCHANT
8144         if (compose->gtkaspell) {
8145                 gtkaspell_delete(compose->gtkaspell);
8146                 compose->gtkaspell = NULL;
8147         }
8148 #endif
8149
8150         if (!compose->batch) {
8151                 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8152                 prefs_common.compose_height = compose->window->allocation.height;
8153         }
8154
8155         if (!gtk_widget_get_parent(compose->paned))
8156                 gtk_widget_destroy(compose->paned);
8157         gtk_widget_destroy(compose->popupmenu);
8158
8159         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8160         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8161         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8162
8163         gtk_widget_destroy(compose->window);
8164         toolbar_destroy(compose->toolbar);
8165         g_free(compose->toolbar);
8166         g_mutex_free(compose->mutex);
8167         g_free(compose);
8168 }
8169
8170 static void compose_attach_info_free(AttachInfo *ainfo)
8171 {
8172         g_free(ainfo->file);
8173         g_free(ainfo->content_type);
8174         g_free(ainfo->name);
8175         g_free(ainfo);
8176 }
8177
8178 static void compose_attach_update_label(Compose *compose)
8179 {
8180         GtkTreeIter iter;
8181         gint i = 1;
8182         gchar *text;
8183         GtkTreeModel *model;
8184         
8185         if(compose == NULL)
8186                 return;
8187                 
8188         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8189         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8190                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8191                 return;
8192         }
8193         
8194         while(gtk_tree_model_iter_next(model, &iter))
8195                 i++;
8196         
8197         text = g_strdup_printf("(%d)", i);
8198         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8199         g_free(text);
8200 }
8201
8202 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8203 {
8204         Compose *compose = (Compose *)data;
8205         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8206         GtkTreeSelection *selection;
8207         GList *sel, *cur;
8208         GtkTreeModel *model;
8209
8210         selection = gtk_tree_view_get_selection(tree_view);
8211         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8212
8213         if (!sel) 
8214                 return;
8215
8216         for (cur = sel; cur != NULL; cur = cur->next) {
8217                 GtkTreePath *path = cur->data;
8218                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8219                                                 (model, cur->data);
8220                 cur->data = ref;
8221                 gtk_tree_path_free(path);
8222         }
8223
8224         for (cur = sel; cur != NULL; cur = cur->next) {
8225                 GtkTreeRowReference *ref = cur->data;
8226                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8227                 GtkTreeIter iter;
8228
8229                 if (gtk_tree_model_get_iter(model, &iter, path))
8230                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8231                 
8232                 gtk_tree_path_free(path);
8233                 gtk_tree_row_reference_free(ref);
8234         }
8235
8236         g_list_free(sel);
8237         compose_attach_update_label(compose);
8238 }
8239
8240 static struct _AttachProperty
8241 {
8242         GtkWidget *window;
8243         GtkWidget *mimetype_entry;
8244         GtkWidget *encoding_optmenu;
8245         GtkWidget *path_entry;
8246         GtkWidget *filename_entry;
8247         GtkWidget *ok_btn;
8248         GtkWidget *cancel_btn;
8249 } attach_prop;
8250
8251 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8252 {       
8253         gtk_tree_path_free((GtkTreePath *)ptr);
8254 }
8255
8256 static void compose_attach_property(GtkAction *action, gpointer data)
8257 {
8258         Compose *compose = (Compose *)data;
8259         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8260         AttachInfo *ainfo;
8261         GtkComboBox *optmenu;
8262         GtkTreeSelection *selection;
8263         GList *sel;
8264         GtkTreeModel *model;
8265         GtkTreeIter iter;
8266         GtkTreePath *path;
8267         static gboolean cancelled;
8268
8269         /* only if one selected */
8270         selection = gtk_tree_view_get_selection(tree_view);
8271         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8272                 return;
8273
8274         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8275         if (!sel)
8276                 return;
8277
8278         path = (GtkTreePath *) sel->data;
8279         gtk_tree_model_get_iter(model, &iter, path);
8280         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8281         
8282         if (!ainfo) {
8283                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8284                 g_list_free(sel);
8285                 return;
8286         }               
8287         g_list_free(sel);
8288
8289         if (!attach_prop.window)
8290                 compose_attach_property_create(&cancelled);
8291         gtk_widget_grab_focus(attach_prop.ok_btn);
8292         gtk_widget_show(attach_prop.window);
8293         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8294
8295         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8296         if (ainfo->encoding == ENC_UNKNOWN)
8297                 combobox_select_by_data(optmenu, ENC_BASE64);
8298         else
8299                 combobox_select_by_data(optmenu, ainfo->encoding);
8300
8301         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8302                            ainfo->content_type ? ainfo->content_type : "");
8303         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8304                            ainfo->file ? ainfo->file : "");
8305         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8306                            ainfo->name ? ainfo->name : "");
8307
8308         for (;;) {
8309                 const gchar *entry_text;
8310                 gchar *text;
8311                 gchar *cnttype = NULL;
8312                 gchar *file = NULL;
8313                 off_t size = 0;
8314
8315                 cancelled = FALSE;
8316                 gtk_main();
8317
8318                 gtk_widget_hide(attach_prop.window);
8319                 
8320                 if (cancelled) 
8321                         break;
8322
8323                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8324                 if (*entry_text != '\0') {
8325                         gchar *p;
8326
8327                         text = g_strstrip(g_strdup(entry_text));
8328                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8329                                 cnttype = g_strdup(text);
8330                                 g_free(text);
8331                         } else {
8332                                 alertpanel_error(_("Invalid MIME type."));
8333                                 g_free(text);
8334                                 continue;
8335                         }
8336                 }
8337
8338                 ainfo->encoding = combobox_get_active_data(optmenu);
8339
8340                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8341                 if (*entry_text != '\0') {
8342                         if (is_file_exist(entry_text) &&
8343                             (size = get_file_size(entry_text)) > 0)
8344                                 file = g_strdup(entry_text);
8345                         else {
8346                                 alertpanel_error
8347                                         (_("File doesn't exist or is empty."));
8348                                 g_free(cnttype);
8349                                 continue;
8350                         }
8351                 }
8352
8353                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8354                 if (*entry_text != '\0') {
8355                         g_free(ainfo->name);
8356                         ainfo->name = g_strdup(entry_text);
8357                 }
8358
8359                 if (cnttype) {
8360                         g_free(ainfo->content_type);
8361                         ainfo->content_type = cnttype;
8362                 }
8363                 if (file) {
8364                         g_free(ainfo->file);
8365                         ainfo->file = file;
8366                 }
8367                 if (size)
8368                         ainfo->size = (goffset)size;
8369
8370                 /* update tree store */
8371                 text = to_human_readable(ainfo->size);
8372                 gtk_tree_model_get_iter(model, &iter, path);
8373                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8374                                    COL_MIMETYPE, ainfo->content_type,
8375                                    COL_SIZE, text,
8376                                    COL_NAME, ainfo->name,
8377                                    -1);
8378                 
8379                 break;
8380         }
8381
8382         gtk_tree_path_free(path);
8383 }
8384
8385 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8386 { \
8387         label = gtk_label_new(str); \
8388         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8389                          GTK_FILL, 0, 0, 0); \
8390         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8391  \
8392         entry = gtk_entry_new(); \
8393         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8394                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8395 }
8396
8397 static void compose_attach_property_create(gboolean *cancelled)
8398 {
8399         GtkWidget *window;
8400         GtkWidget *vbox;
8401         GtkWidget *table;
8402         GtkWidget *label;
8403         GtkWidget *mimetype_entry;
8404         GtkWidget *hbox;
8405         GtkWidget *optmenu;
8406         GtkListStore *optmenu_menu;
8407         GtkWidget *path_entry;
8408         GtkWidget *filename_entry;
8409         GtkWidget *hbbox;
8410         GtkWidget *ok_btn;
8411         GtkWidget *cancel_btn;
8412         GList     *mime_type_list, *strlist;
8413         GtkTreeIter iter;
8414
8415         debug_print("Creating attach_property window...\n");
8416
8417         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8418         gtk_widget_set_size_request(window, 480, -1);
8419         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8420         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8421         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8422         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8423         g_signal_connect(G_OBJECT(window), "delete_event",
8424                          G_CALLBACK(attach_property_delete_event),
8425                          cancelled);
8426         g_signal_connect(G_OBJECT(window), "key_press_event",
8427                          G_CALLBACK(attach_property_key_pressed),
8428                          cancelled);
8429
8430         vbox = gtk_vbox_new(FALSE, 8);
8431         gtk_container_add(GTK_CONTAINER(window), vbox);
8432
8433         table = gtk_table_new(4, 2, FALSE);
8434         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8435         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8436         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8437
8438         label = gtk_label_new(_("MIME type")); 
8439         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8440                          GTK_FILL, 0, 0, 0); 
8441         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8442         mimetype_entry = gtk_combo_box_entry_new_text(); 
8443         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8444                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8445                          
8446         /* stuff with list */
8447         mime_type_list = procmime_get_mime_type_list();
8448         strlist = NULL;
8449         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8450                 MimeType *type = (MimeType *) mime_type_list->data;
8451                 gchar *tmp;
8452
8453                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8454
8455                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8456                         g_free(tmp);
8457                 else
8458                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8459                                         (GCompareFunc)strcmp2);
8460         }
8461
8462         for (mime_type_list = strlist; mime_type_list != NULL; 
8463                 mime_type_list = mime_type_list->next) {
8464                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8465                 g_free(mime_type_list->data);
8466         }
8467         g_list_free(strlist);
8468         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8469         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8470
8471         label = gtk_label_new(_("Encoding"));
8472         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8473                          GTK_FILL, 0, 0, 0);
8474         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8475
8476         hbox = gtk_hbox_new(FALSE, 0);
8477         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8478                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8479
8480         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8481         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8482
8483         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8484         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8485         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8486         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8487         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8488
8489         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8490
8491         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8492         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8493
8494         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8495                                       &ok_btn, GTK_STOCK_OK,
8496                                       NULL, NULL);
8497         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8498         gtk_widget_grab_default(ok_btn);
8499
8500         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8501                          G_CALLBACK(attach_property_ok),
8502                          cancelled);
8503         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8504                          G_CALLBACK(attach_property_cancel),
8505                          cancelled);
8506
8507         gtk_widget_show_all(vbox);
8508
8509         attach_prop.window           = window;
8510         attach_prop.mimetype_entry   = mimetype_entry;
8511         attach_prop.encoding_optmenu = optmenu;
8512         attach_prop.path_entry       = path_entry;
8513         attach_prop.filename_entry   = filename_entry;
8514         attach_prop.ok_btn           = ok_btn;
8515         attach_prop.cancel_btn       = cancel_btn;
8516 }
8517
8518 #undef SET_LABEL_AND_ENTRY
8519
8520 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8521 {
8522         *cancelled = FALSE;
8523         gtk_main_quit();
8524 }
8525
8526 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8527 {
8528         *cancelled = TRUE;
8529         gtk_main_quit();
8530 }
8531
8532 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8533                                          gboolean *cancelled)
8534 {
8535         *cancelled = TRUE;
8536         gtk_main_quit();
8537
8538         return TRUE;
8539 }
8540
8541 static gboolean attach_property_key_pressed(GtkWidget *widget,
8542                                             GdkEventKey *event,
8543                                             gboolean *cancelled)
8544 {
8545         if (event && event->keyval == GDK_Escape) {
8546                 *cancelled = TRUE;
8547                 gtk_main_quit();
8548         }
8549         if (event && event->keyval == GDK_Return) {
8550                 *cancelled = FALSE;
8551                 gtk_main_quit();
8552         }
8553         return FALSE;
8554 }
8555
8556 static void compose_exec_ext_editor(Compose *compose)
8557 {
8558 #ifdef G_OS_UNIX
8559         gchar *tmp;
8560         pid_t pid;
8561         gint pipe_fds[2];
8562
8563         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8564                               G_DIR_SEPARATOR, compose);
8565
8566         if (pipe(pipe_fds) < 0) {
8567                 perror("pipe");
8568                 g_free(tmp);
8569                 return;
8570         }
8571
8572         if ((pid = fork()) < 0) {
8573                 perror("fork");
8574                 g_free(tmp);
8575                 return;
8576         }
8577
8578         if (pid != 0) {
8579                 /* close the write side of the pipe */
8580                 close(pipe_fds[1]);
8581
8582                 compose->exteditor_file    = g_strdup(tmp);
8583                 compose->exteditor_pid     = pid;
8584
8585                 compose_set_ext_editor_sensitive(compose, FALSE);
8586
8587 #ifndef G_OS_WIN32
8588                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8589 #else
8590                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8591 #endif
8592                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8593                                                         G_IO_IN,
8594                                                         compose_input_cb,
8595                                                         compose);
8596         } else {        /* process-monitoring process */
8597                 pid_t pid_ed;
8598
8599                 if (setpgid(0, 0))
8600                         perror("setpgid");
8601
8602                 /* close the read side of the pipe */
8603                 close(pipe_fds[0]);
8604
8605                 if (compose_write_body_to_file(compose, tmp) < 0) {
8606                         fd_write_all(pipe_fds[1], "2\n", 2);
8607                         _exit(1);
8608                 }
8609
8610                 pid_ed = compose_exec_ext_editor_real(tmp);
8611                 if (pid_ed < 0) {
8612                         fd_write_all(pipe_fds[1], "1\n", 2);
8613                         _exit(1);
8614                 }
8615
8616                 /* wait until editor is terminated */
8617                 waitpid(pid_ed, NULL, 0);
8618
8619                 fd_write_all(pipe_fds[1], "0\n", 2);
8620
8621                 close(pipe_fds[1]);
8622                 _exit(0);
8623         }
8624
8625         g_free(tmp);
8626 #endif /* G_OS_UNIX */
8627 }
8628
8629 #ifdef G_OS_UNIX
8630 static gint compose_exec_ext_editor_real(const gchar *file)
8631 {
8632         gchar buf[1024];
8633         gchar *p;
8634         gchar **cmdline;
8635         pid_t pid;
8636
8637         cm_return_val_if_fail(file != NULL, -1);
8638
8639         if ((pid = fork()) < 0) {
8640                 perror("fork");
8641                 return -1;
8642         }
8643
8644         if (pid != 0) return pid;
8645
8646         /* grandchild process */
8647
8648         if (setpgid(0, getppid()))
8649                 perror("setpgid");
8650
8651         if (prefs_common_get_ext_editor_cmd() &&
8652             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8653             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8654                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8655         } else {
8656                 if (prefs_common_get_ext_editor_cmd())
8657                         g_warning("External editor command-line is invalid: '%s'\n",
8658                                   prefs_common_get_ext_editor_cmd());
8659                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8660         }
8661
8662         cmdline = strsplit_with_quote(buf, " ", 1024);
8663         execvp(cmdline[0], cmdline);
8664
8665         perror("execvp");
8666         g_strfreev(cmdline);
8667
8668         _exit(1);
8669 }
8670
8671 static gboolean compose_ext_editor_kill(Compose *compose)
8672 {
8673         pid_t pgid = compose->exteditor_pid * -1;
8674         gint ret;
8675
8676         ret = kill(pgid, 0);
8677
8678         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8679                 AlertValue val;
8680                 gchar *msg;
8681
8682                 msg = g_strdup_printf
8683                         (_("The external editor is still working.\n"
8684                            "Force terminating the process?\n"
8685                            "process group id: %d"), -pgid);
8686                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8687                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8688                         
8689                 g_free(msg);
8690
8691                 if (val == G_ALERTALTERNATE) {
8692                         g_source_remove(compose->exteditor_tag);
8693                         g_io_channel_shutdown(compose->exteditor_ch,
8694                                               FALSE, NULL);
8695                         g_io_channel_unref(compose->exteditor_ch);
8696
8697                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8698                         waitpid(compose->exteditor_pid, NULL, 0);
8699
8700                         g_warning("Terminated process group id: %d", -pgid);
8701                         g_warning("Temporary file: %s",
8702                                   compose->exteditor_file);
8703
8704                         compose_set_ext_editor_sensitive(compose, TRUE);
8705
8706                         g_free(compose->exteditor_file);
8707                         compose->exteditor_file    = NULL;
8708                         compose->exteditor_pid     = -1;
8709                         compose->exteditor_ch      = NULL;
8710                         compose->exteditor_tag     = -1;
8711                 } else
8712                         return FALSE;
8713         }
8714
8715         return TRUE;
8716 }
8717
8718 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8719                                  gpointer data)
8720 {
8721         gchar buf[3] = "3";
8722         Compose *compose = (Compose *)data;
8723         gsize bytes_read;
8724
8725         debug_print(_("Compose: input from monitoring process\n"));
8726
8727         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8728
8729         g_io_channel_shutdown(source, FALSE, NULL);
8730         g_io_channel_unref(source);
8731
8732         waitpid(compose->exteditor_pid, NULL, 0);
8733
8734         if (buf[0] == '0') {            /* success */
8735                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8736                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8737
8738                 gtk_text_buffer_set_text(buffer, "", -1);
8739                 compose_insert_file(compose, compose->exteditor_file);
8740                 compose_changed_cb(NULL, compose);
8741                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8742
8743                 if (claws_unlink(compose->exteditor_file) < 0)
8744                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8745         } else if (buf[0] == '1') {     /* failed */
8746                 g_warning("Couldn't exec external editor\n");
8747                 if (claws_unlink(compose->exteditor_file) < 0)
8748                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8749         } else if (buf[0] == '2') {
8750                 g_warning("Couldn't write to file\n");
8751         } else if (buf[0] == '3') {
8752                 g_warning("Pipe read failed\n");
8753         }
8754
8755         compose_set_ext_editor_sensitive(compose, TRUE);
8756
8757         g_free(compose->exteditor_file);
8758         compose->exteditor_file    = NULL;
8759         compose->exteditor_pid     = -1;
8760         compose->exteditor_ch      = NULL;
8761         compose->exteditor_tag     = -1;
8762
8763         return FALSE;
8764 }
8765
8766 static void compose_set_ext_editor_sensitive(Compose *compose,
8767                                              gboolean sensitive)
8768 {
8769         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8770         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8771         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8772         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8773         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8774         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8775         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8776
8777         gtk_widget_set_sensitive(compose->text,                       sensitive);
8778         if (compose->toolbar->send_btn)
8779                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8780         if (compose->toolbar->sendl_btn)
8781                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8782         if (compose->toolbar->draft_btn)
8783                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8784         if (compose->toolbar->insert_btn)
8785                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8786         if (compose->toolbar->sig_btn)
8787                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8788         if (compose->toolbar->exteditor_btn)
8789                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8790         if (compose->toolbar->linewrap_current_btn)
8791                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8792         if (compose->toolbar->linewrap_all_btn)
8793                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8794 }
8795 #endif /* G_OS_UNIX */
8796
8797 /**
8798  * compose_undo_state_changed:
8799  *
8800  * Change the sensivity of the menuentries undo and redo
8801  **/
8802 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8803                                        gint redo_state, gpointer data)
8804 {
8805         Compose *compose = (Compose *)data;
8806
8807         switch (undo_state) {
8808         case UNDO_STATE_TRUE:
8809                 if (!undostruct->undo_state) {
8810                         undostruct->undo_state = TRUE;
8811                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8812                 }
8813                 break;
8814         case UNDO_STATE_FALSE:
8815                 if (undostruct->undo_state) {
8816                         undostruct->undo_state = FALSE;
8817                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8818                 }
8819                 break;
8820         case UNDO_STATE_UNCHANGED:
8821                 break;
8822         case UNDO_STATE_REFRESH:
8823                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8824                 break;
8825         default:
8826                 g_warning("Undo state not recognized");
8827                 break;
8828         }
8829
8830         switch (redo_state) {
8831         case UNDO_STATE_TRUE:
8832                 if (!undostruct->redo_state) {
8833                         undostruct->redo_state = TRUE;
8834                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8835                 }
8836                 break;
8837         case UNDO_STATE_FALSE:
8838                 if (undostruct->redo_state) {
8839                         undostruct->redo_state = FALSE;
8840                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8841                 }
8842                 break;
8843         case UNDO_STATE_UNCHANGED:
8844                 break;
8845         case UNDO_STATE_REFRESH:
8846                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8847                 break;
8848         default:
8849                 g_warning("Redo state not recognized");
8850                 break;
8851         }
8852 }
8853
8854 /* callback functions */
8855
8856 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8857  * includes "non-client" (windows-izm) in calculation, so this calculation
8858  * may not be accurate.
8859  */
8860 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8861                                         GtkAllocation *allocation,
8862                                         GtkSHRuler *shruler)
8863 {
8864         if (prefs_common.show_ruler) {
8865                 gint char_width = 0, char_height = 0;
8866                 gint line_width_in_chars;
8867
8868                 gtkut_get_font_size(GTK_WIDGET(widget),
8869                                     &char_width, &char_height);
8870                 line_width_in_chars =
8871                         (allocation->width - allocation->x) / char_width;
8872
8873                 /* got the maximum */
8874                 gtk_ruler_set_range(GTK_RULER(shruler),
8875                                     0.0, line_width_in_chars, 0,
8876                                     /*line_width_in_chars*/ char_width);
8877         }
8878
8879         return TRUE;
8880 }
8881
8882 static void account_activated(GtkComboBox *optmenu, gpointer data)
8883 {
8884         Compose *compose = (Compose *)data;
8885
8886         PrefsAccount *ac;
8887         gchar *folderidentifier;
8888         gint account_id = 0;
8889         GtkTreeModel *menu;
8890         GtkTreeIter iter;
8891
8892         /* Get ID of active account in the combo box */
8893         menu = gtk_combo_box_get_model(optmenu);
8894         gtk_combo_box_get_active_iter(optmenu, &iter);
8895         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8896
8897         ac = account_find_from_id(account_id);
8898         cm_return_if_fail(ac != NULL);
8899
8900         if (ac != compose->account)
8901                 compose_select_account(compose, ac, FALSE);
8902
8903         /* Set message save folder */
8904         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8905                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8906         }
8907         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8908                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8909                            
8910         compose_set_save_to(compose, NULL);
8911         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8912                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8913                                   (compose->account, F_OUTBOX));
8914                 compose_set_save_to(compose, folderidentifier);
8915                 g_free(folderidentifier);
8916         }
8917 }
8918
8919 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8920                             GtkTreeViewColumn *column, Compose *compose)
8921 {
8922         compose_attach_property(NULL, compose);
8923 }
8924
8925 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8926                                       gpointer data)
8927 {
8928         Compose *compose = (Compose *)data;
8929         GtkTreeSelection *attach_selection;
8930         gint attach_nr_selected;
8931         
8932         if (!event) return FALSE;
8933
8934         if (event->button == 3) {
8935                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8936                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8937                         
8938                 if (attach_nr_selected > 0)
8939                 {
8940                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8941                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8942                 } else {
8943                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8944                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8945                 }
8946                         
8947                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8948                                NULL, NULL, event->button, event->time);
8949                 return TRUE;                           
8950         }
8951
8952         return FALSE;
8953 }
8954
8955 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8956                                    gpointer data)
8957 {
8958         Compose *compose = (Compose *)data;
8959
8960         if (!event) return FALSE;
8961
8962         switch (event->keyval) {
8963         case GDK_Delete:
8964                 compose_attach_remove_selected(NULL, compose);
8965                 break;
8966         }
8967         return FALSE;
8968 }
8969
8970 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8971 {
8972         toolbar_comp_set_sensitive(compose, allow);
8973         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8974         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8975 #if USE_ENCHANT
8976         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8977 #endif  
8978         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8979         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8980         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8981         
8982         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8983
8984 }
8985
8986 static void compose_send_cb(GtkAction *action, gpointer data)
8987 {
8988         Compose *compose = (Compose *)data;
8989         
8990         if (prefs_common.work_offline && 
8991             !inc_offline_should_override(TRUE,
8992                 _("Claws Mail needs network access in order "
8993                   "to send this email.")))
8994                 return;
8995         
8996         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8997                 g_source_remove(compose->draft_timeout_tag);
8998                 compose->draft_timeout_tag = -1;
8999         }
9000
9001         compose_send(compose);
9002 }
9003
9004 static void compose_send_later_cb(GtkAction *action, gpointer data)
9005 {
9006         Compose *compose = (Compose *)data;
9007         gint val;
9008
9009         inc_lock();
9010         compose_allow_user_actions(compose, FALSE);
9011         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9012         compose_allow_user_actions(compose, TRUE);
9013         inc_unlock();
9014
9015         if (!val) {
9016                 compose_close(compose);
9017         } else if (val == -1) {
9018                 alertpanel_error(_("Could not queue message."));
9019         } else if (val == -2) {
9020                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9021         } else if (val == -3) {
9022                 if (privacy_peek_error())
9023                 alertpanel_error(_("Could not queue message for sending:\n\n"
9024                                    "Signature failed: %s"), privacy_get_error());
9025         } else if (val == -4) {
9026                 alertpanel_error(_("Could not queue message for sending:\n\n"
9027                                    "Charset conversion failed."));
9028         } else if (val == -5) {
9029                 alertpanel_error(_("Could not queue message for sending:\n\n"
9030                                    "Couldn't get recipient encryption key."));
9031         } else if (val == -6) {
9032                 /* silent error */
9033         }
9034         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9035 }
9036
9037 #define DRAFTED_AT_EXIT "drafted_at_exit"
9038 static void compose_register_draft(MsgInfo *info)
9039 {
9040         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9041                                       DRAFTED_AT_EXIT, NULL);
9042         FILE *fp = g_fopen(filepath, "ab");
9043         
9044         if (fp) {
9045                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9046                                 info->msgnum);
9047                 fclose(fp);
9048         }
9049                 
9050         g_free(filepath);       
9051 }
9052
9053 gboolean compose_draft (gpointer data, guint action) 
9054 {
9055         Compose *compose = (Compose *)data;
9056         FolderItem *draft;
9057         gchar *tmp;
9058         gint msgnum;
9059         MsgFlags flag = {0, 0};
9060         static gboolean lock = FALSE;
9061         MsgInfo *newmsginfo;
9062         FILE *fp;
9063         gboolean target_locked = FALSE;
9064         gboolean err = FALSE;
9065
9066         if (lock) return FALSE;
9067
9068         if (compose->sending)
9069                 return TRUE;
9070
9071         draft = account_get_special_folder(compose->account, F_DRAFT);
9072         cm_return_val_if_fail(draft != NULL, FALSE);
9073         
9074         if (!g_mutex_trylock(compose->mutex)) {
9075                 /* we don't want to lock the mutex once it's available,
9076                  * because as the only other part of compose.c locking
9077                  * it is compose_close - which means once unlocked,
9078                  * the compose struct will be freed */
9079                 debug_print("couldn't lock mutex, probably sending\n");
9080                 return FALSE;
9081         }
9082         
9083         lock = TRUE;
9084
9085         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9086                               G_DIR_SEPARATOR, compose);
9087         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9088                 FILE_OP_ERROR(tmp, "fopen");
9089                 goto warn_err;
9090         }
9091
9092         /* chmod for security */
9093         if (change_file_mode_rw(fp, tmp) < 0) {
9094                 FILE_OP_ERROR(tmp, "chmod");
9095                 g_warning("can't change file mode\n");
9096         }
9097
9098         /* Save draft infos */
9099         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9100         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9101
9102         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9103                 gchar *savefolderid;
9104
9105                 savefolderid = compose_get_save_to(compose);
9106                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9107                 g_free(savefolderid);
9108         }
9109         if (compose->return_receipt) {
9110                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9111         }
9112         if (compose->privacy_system) {
9113                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9114                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9115                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9116         }
9117
9118         /* Message-ID of message replying to */
9119         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9120                 gchar *folderid;
9121                 
9122                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9123                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9124                 g_free(folderid);
9125         }
9126         /* Message-ID of message forwarding to */
9127         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9128                 gchar *folderid;
9129                 
9130                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9131                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9132                 g_free(folderid);
9133         }
9134
9135         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9136         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9137
9138         /* end of headers */
9139         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9140
9141         if (err) {
9142                 fclose(fp);
9143                 goto warn_err;
9144         }
9145
9146         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9147                 fclose(fp);
9148                 goto warn_err;
9149         }
9150         if (fclose(fp) == EOF) {
9151                 goto warn_err;
9152         }
9153         
9154         if (compose->targetinfo) {
9155                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9156                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9157         }
9158         flag.tmp_flags = MSG_DRAFT;
9159
9160         folder_item_scan(draft);
9161         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9162                 MsgInfo *tmpinfo = NULL;
9163                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9164                 if (compose->msgid) {
9165                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9166                 }
9167                 if (tmpinfo) {
9168                         msgnum = tmpinfo->msgnum;
9169                         procmsg_msginfo_free(tmpinfo);
9170                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9171                 } else {
9172                         debug_print("didn't get draft msgnum after scanning\n");
9173                 }
9174         } else {
9175                 debug_print("got draft msgnum %d from adding\n", msgnum);
9176         }
9177         if (msgnum < 0) {
9178 warn_err:
9179                 claws_unlink(tmp);
9180                 g_free(tmp);
9181                 if (action != COMPOSE_AUTO_SAVE) {
9182                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9183                                 alertpanel_error(_("Could not save draft."));
9184                         else {
9185                                 AlertValue val;
9186                                 gtkut_window_popup(compose->window);
9187                                 val = alertpanel_full(_("Could not save draft"),
9188                                         _("Could not save draft.\n"
9189                                         "Do you want to cancel exit or discard this email?"),
9190                                           _("_Cancel exit"), _("_Discard email"), NULL,
9191                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9192                                 if (val == G_ALERTALTERNATE) {
9193                                         lock = FALSE;
9194                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9195                                         compose_close(compose);
9196                                         return TRUE;
9197                                 } else {
9198                                         lock = FALSE;
9199                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9200                                         return FALSE;
9201                                 }
9202                         }
9203                 }
9204                 goto unlock;
9205         }
9206         g_free(tmp);
9207
9208         if (compose->mode == COMPOSE_REEDIT) {
9209                 compose_remove_reedit_target(compose, TRUE);
9210         }
9211
9212         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9213
9214         if (newmsginfo) {
9215                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9216                 if (target_locked)
9217                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9218                 else
9219                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9220                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9221                         procmsg_msginfo_set_flags(newmsginfo, 0,
9222                                                   MSG_HAS_ATTACHMENT);
9223
9224                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9225                         compose_register_draft(newmsginfo);
9226                 }
9227                 procmsg_msginfo_free(newmsginfo);
9228         }
9229         
9230         folder_item_scan(draft);
9231         
9232         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9233                 lock = FALSE;
9234                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9235                 compose_close(compose);
9236                 return TRUE;
9237         } else {
9238                 struct stat s;
9239                 gchar *path;
9240
9241                 path = folder_item_fetch_msg(draft, msgnum);
9242                 if (path == NULL) {
9243                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9244                         goto unlock;
9245                 }
9246                 if (g_stat(path, &s) < 0) {
9247                         FILE_OP_ERROR(path, "stat");
9248                         g_free(path);
9249                         goto unlock;
9250                 }
9251                 g_free(path);
9252
9253                 procmsg_msginfo_free(compose->targetinfo);
9254                 compose->targetinfo = procmsg_msginfo_new();
9255                 compose->targetinfo->msgnum = msgnum;
9256                 compose->targetinfo->size = (goffset)s.st_size;
9257                 compose->targetinfo->mtime = s.st_mtime;
9258                 compose->targetinfo->folder = draft;
9259                 if (target_locked)
9260                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9261                 compose->mode = COMPOSE_REEDIT;
9262                 
9263                 if (action == COMPOSE_AUTO_SAVE) {
9264                         compose->autosaved_draft = compose->targetinfo;
9265                 }
9266                 compose->modified = FALSE;
9267                 compose_set_title(compose);
9268         }
9269 unlock:
9270         lock = FALSE;
9271         g_mutex_unlock(compose->mutex);
9272         return TRUE;
9273 }
9274
9275 void compose_clear_exit_drafts(void)
9276 {
9277         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9278                                       DRAFTED_AT_EXIT, NULL);
9279         if (is_file_exist(filepath))
9280                 claws_unlink(filepath);
9281         
9282         g_free(filepath);
9283 }
9284
9285 void compose_reopen_exit_drafts(void)
9286 {
9287         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9288                                       DRAFTED_AT_EXIT, NULL);
9289         FILE *fp = g_fopen(filepath, "rb");
9290         gchar buf[1024];
9291         
9292         if (fp) {
9293                 while (fgets(buf, sizeof(buf), fp)) {
9294                         gchar **parts = g_strsplit(buf, "\t", 2);
9295                         const gchar *folder = parts[0];
9296                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9297                         
9298                         if (folder && *folder && msgnum > -1) {
9299                                 FolderItem *item = folder_find_item_from_identifier(folder);
9300                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9301                                 if (info)
9302                                         compose_reedit(info, FALSE);
9303                         }
9304                         g_strfreev(parts);
9305                 }       
9306                 fclose(fp);
9307         }       
9308         g_free(filepath);
9309         compose_clear_exit_drafts();
9310 }
9311
9312 static void compose_save_cb(GtkAction *action, gpointer data)
9313 {
9314         Compose *compose = (Compose *)data;
9315         compose_draft(compose, COMPOSE_KEEP_EDITING);
9316         compose->rmode = COMPOSE_REEDIT;
9317 }
9318
9319 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9320 {
9321         if (compose && file_list) {
9322                 GList *tmp;
9323
9324                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9325                         gchar *file = (gchar *) tmp->data;
9326                         gchar *utf8_filename = conv_filename_to_utf8(file);
9327                         compose_attach_append(compose, file, utf8_filename, NULL);
9328                         compose_changed_cb(NULL, compose);
9329                         if (free_data) {
9330                         g_free(file);
9331                                 tmp->data = NULL;
9332                         }
9333                         g_free(utf8_filename);
9334                 }
9335         }
9336 }
9337
9338 static void compose_attach_cb(GtkAction *action, gpointer data)
9339 {
9340         Compose *compose = (Compose *)data;
9341         GList *file_list;
9342
9343         if (compose->redirect_filename != NULL)
9344                 return;
9345
9346         file_list = filesel_select_multiple_files_open(_("Select file"));
9347
9348         if (file_list) {
9349                 compose_attach_from_list(compose, file_list, TRUE);
9350                 g_list_free(file_list);
9351         }
9352 }
9353
9354 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9355 {
9356         Compose *compose = (Compose *)data;
9357         GList *file_list;
9358
9359         file_list = filesel_select_multiple_files_open(_("Select file"));
9360
9361         if (file_list) {
9362                 GList *tmp;
9363
9364                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9365                         gchar *file = (gchar *) tmp->data;
9366                         gchar *filedup = g_strdup(file);
9367                         gchar *shortfile = g_path_get_basename(filedup);
9368                         ComposeInsertResult res;
9369
9370                         res = compose_insert_file(compose, file);
9371                         if (res == COMPOSE_INSERT_READ_ERROR) {
9372                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9373                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9374                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9375                                                    "for the current encoding, insertion may be incorrect."), shortfile);
9376                         }
9377                         g_free(shortfile);
9378                         g_free(filedup);
9379                         g_free(file);
9380                 }
9381                 g_list_free(file_list);
9382         }
9383 }
9384
9385 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9386 {
9387         Compose *compose = (Compose *)data;
9388
9389         compose_insert_sig(compose, FALSE);
9390 }
9391
9392 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9393                               gpointer data)
9394 {
9395         gint x, y;
9396         Compose *compose = (Compose *)data;
9397
9398         gtkut_widget_get_uposition(widget, &x, &y);
9399         if (!compose->batch) {
9400                 prefs_common.compose_x = x;
9401                 prefs_common.compose_y = y;
9402         }
9403         if (compose->sending || compose->updating)
9404                 return TRUE;
9405         compose_close_cb(NULL, compose);
9406         return TRUE;
9407 }
9408
9409 void compose_close_toolbar(Compose *compose)
9410 {
9411         compose_close_cb(NULL, compose);
9412 }
9413
9414 static void compose_close_cb(GtkAction *action, gpointer data)
9415 {
9416         Compose *compose = (Compose *)data;
9417         AlertValue val;
9418
9419 #ifdef G_OS_UNIX
9420         if (compose->exteditor_tag != -1) {
9421                 if (!compose_ext_editor_kill(compose))
9422                         return;
9423         }
9424 #endif
9425
9426         if (compose->modified) {
9427                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9428                 if (!g_mutex_trylock(compose->mutex)) {
9429                         /* we don't want to lock the mutex once it's available,
9430                          * because as the only other part of compose.c locking
9431                          * it is compose_close - which means once unlocked,
9432                          * the compose struct will be freed */
9433                         debug_print("couldn't lock mutex, probably sending\n");
9434                         return;
9435                 }
9436                 if (!reedit) {
9437                         val = alertpanel(_("Discard message"),
9438                                  _("This message has been modified. Discard it?"),
9439                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9440                 } else {
9441                         val = alertpanel(_("Save changes"),
9442                                  _("This message has been modified. Save the latest changes?"),
9443                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9444                 }
9445                 g_mutex_unlock(compose->mutex);
9446                 switch (val) {
9447                 case G_ALERTDEFAULT:
9448                         if (prefs_common.autosave && !reedit)
9449                                 compose_remove_draft(compose);                  
9450                         break;
9451                 case G_ALERTALTERNATE:
9452                         compose_draft(data, COMPOSE_QUIT_EDITING);
9453                         return;
9454                 default:
9455                         return;
9456                 }
9457         }
9458
9459         compose_close(compose);
9460 }
9461
9462 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9463 {
9464         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9465         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9466         Compose *compose = (Compose *) data;
9467
9468         if (active)
9469                 compose->out_encoding = (CharSet)value;
9470 }
9471
9472 static void compose_address_cb(GtkAction *action, gpointer data)
9473 {
9474         Compose *compose = (Compose *)data;
9475
9476         addressbook_open(compose);
9477 }
9478
9479 static void about_show_cb(GtkAction *action, gpointer data)
9480 {
9481         about_show();
9482 }
9483
9484 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9485 {
9486         Compose *compose = (Compose *)data;
9487         Template *tmpl;
9488         gchar *msg;
9489         AlertValue val;
9490
9491         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9492         cm_return_if_fail(tmpl != NULL);
9493
9494         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9495                               tmpl->name);
9496         val = alertpanel(_("Apply template"), msg,
9497                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9498         g_free(msg);
9499
9500         if (val == G_ALERTDEFAULT)
9501                 compose_template_apply(compose, tmpl, TRUE);
9502         else if (val == G_ALERTALTERNATE)
9503                 compose_template_apply(compose, tmpl, FALSE);
9504 }
9505
9506 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9507 {
9508         Compose *compose = (Compose *)data;
9509
9510         compose_exec_ext_editor(compose);
9511 }
9512
9513 static void compose_undo_cb(GtkAction *action, gpointer data)
9514 {
9515         Compose *compose = (Compose *)data;
9516         gboolean prev_autowrap = compose->autowrap;
9517
9518         compose->autowrap = FALSE;
9519         undo_undo(compose->undostruct);
9520         compose->autowrap = prev_autowrap;
9521 }
9522
9523 static void compose_redo_cb(GtkAction *action, gpointer data)
9524 {
9525         Compose *compose = (Compose *)data;
9526         gboolean prev_autowrap = compose->autowrap;
9527         
9528         compose->autowrap = FALSE;
9529         undo_redo(compose->undostruct);
9530         compose->autowrap = prev_autowrap;
9531 }
9532
9533 static void entry_cut_clipboard(GtkWidget *entry)
9534 {
9535         if (GTK_IS_EDITABLE(entry))
9536                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9537         else if (GTK_IS_TEXT_VIEW(entry))
9538                 gtk_text_buffer_cut_clipboard(
9539                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9540                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9541                         TRUE);
9542 }
9543
9544 static void entry_copy_clipboard(GtkWidget *entry)
9545 {
9546         if (GTK_IS_EDITABLE(entry))
9547                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9548         else if (GTK_IS_TEXT_VIEW(entry))
9549                 gtk_text_buffer_copy_clipboard(
9550                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9551                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9552 }
9553
9554 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9555                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9556 {
9557         if (GTK_IS_TEXT_VIEW(entry)) {
9558                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9559                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9560                 GtkTextIter start_iter, end_iter;
9561                 gint start, end;
9562                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9563
9564                 if (contents == NULL)
9565                         return;
9566         
9567                 /* we shouldn't delete the selection when middle-click-pasting, or we
9568                  * can't mid-click-paste our own selection */
9569                 if (clip != GDK_SELECTION_PRIMARY) {
9570                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9571                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9572                 }
9573                 
9574                 if (insert_place == NULL) {
9575                         /* if insert_place isn't specified, insert at the cursor.
9576                          * used for Ctrl-V pasting */
9577                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9578                         start = gtk_text_iter_get_offset(&start_iter);
9579                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9580                 } else {
9581                         /* if insert_place is specified, paste here.
9582                          * used for mid-click-pasting */
9583                         start = gtk_text_iter_get_offset(insert_place);
9584                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9585                         if (prefs_common.primary_paste_unselects)
9586                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9587                 }
9588                 
9589                 if (!wrap) {
9590                         /* paste unwrapped: mark the paste so it's not wrapped later */
9591                         end = start + strlen(contents);
9592                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9593                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9594                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9595                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9596                         /* rewrap paragraph now (after a mid-click-paste) */
9597                         mark_start = gtk_text_buffer_get_insert(buffer);
9598                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9599                         gtk_text_iter_backward_char(&start_iter);
9600                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9601                 }
9602         } else if (GTK_IS_EDITABLE(entry))
9603                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9604
9605         compose->modified = TRUE;
9606 }
9607
9608 static void entry_allsel(GtkWidget *entry)
9609 {
9610         if (GTK_IS_EDITABLE(entry))
9611                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9612         else if (GTK_IS_TEXT_VIEW(entry)) {
9613                 GtkTextIter startiter, enditer;
9614                 GtkTextBuffer *textbuf;
9615
9616                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9617                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9618                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9619
9620                 gtk_text_buffer_move_mark_by_name(textbuf, 
9621                         "selection_bound", &startiter);
9622                 gtk_text_buffer_move_mark_by_name(textbuf, 
9623                         "insert", &enditer);
9624         }
9625 }
9626
9627 static void compose_cut_cb(GtkAction *action, gpointer data)
9628 {
9629         Compose *compose = (Compose *)data;
9630         if (compose->focused_editable 
9631 #ifndef GENERIC_UMPC
9632             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9633 #endif
9634             )
9635                 entry_cut_clipboard(compose->focused_editable);
9636 }
9637
9638 static void compose_copy_cb(GtkAction *action, gpointer data)
9639 {
9640         Compose *compose = (Compose *)data;
9641         if (compose->focused_editable 
9642 #ifndef GENERIC_UMPC
9643             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9644 #endif
9645             )
9646                 entry_copy_clipboard(compose->focused_editable);
9647 }
9648
9649 static void compose_paste_cb(GtkAction *action, gpointer data)
9650 {
9651         Compose *compose = (Compose *)data;
9652         gint prev_autowrap;
9653         GtkTextBuffer *buffer;
9654         BLOCK_WRAP();
9655         if (compose->focused_editable &&
9656             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9657                 entry_paste_clipboard(compose, compose->focused_editable, 
9658                                 prefs_common.linewrap_pastes,
9659                                 GDK_SELECTION_CLIPBOARD, NULL);
9660         UNBLOCK_WRAP();
9661 }
9662
9663 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9664 {
9665         Compose *compose = (Compose *)data;
9666         gint wrap_quote = prefs_common.linewrap_quote;
9667         if (compose->focused_editable 
9668 #ifndef GENERIC_UMPC
9669             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9670 #endif
9671             ) {
9672                 /* let text_insert() (called directly or at a later time
9673                  * after the gtk_editable_paste_clipboard) know that 
9674                  * text is to be inserted as a quotation. implemented
9675                  * by using a simple refcount... */
9676                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9677                                                 G_OBJECT(compose->focused_editable),
9678                                                 "paste_as_quotation"));
9679                 g_object_set_data(G_OBJECT(compose->focused_editable),
9680                                     "paste_as_quotation",
9681                                     GINT_TO_POINTER(paste_as_quotation + 1));
9682                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9683                 entry_paste_clipboard(compose, compose->focused_editable, 
9684                                 prefs_common.linewrap_pastes,
9685                                 GDK_SELECTION_CLIPBOARD, NULL);
9686                 prefs_common.linewrap_quote = wrap_quote;
9687         }
9688 }
9689
9690 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9691 {
9692         Compose *compose = (Compose *)data;
9693         gint prev_autowrap;
9694         GtkTextBuffer *buffer;
9695         BLOCK_WRAP();
9696         if (compose->focused_editable 
9697 #ifndef GENERIC_UMPC
9698             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9699 #endif
9700             )
9701                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9702                         GDK_SELECTION_CLIPBOARD, NULL);
9703         UNBLOCK_WRAP();
9704 }
9705
9706 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9707 {
9708         Compose *compose = (Compose *)data;
9709         gint prev_autowrap;
9710         GtkTextBuffer *buffer;
9711         BLOCK_WRAP();
9712         if (compose->focused_editable 
9713 #ifndef GENERIC_UMPC
9714             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9715 #endif
9716             )
9717                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9718                         GDK_SELECTION_CLIPBOARD, NULL);
9719         UNBLOCK_WRAP();
9720 }
9721
9722 static void compose_allsel_cb(GtkAction *action, gpointer data)
9723 {
9724         Compose *compose = (Compose *)data;
9725         if (compose->focused_editable 
9726 #ifndef GENERIC_UMPC
9727             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9728 #endif
9729             )
9730                 entry_allsel(compose->focused_editable);
9731 }
9732
9733 static void textview_move_beginning_of_line (GtkTextView *text)
9734 {
9735         GtkTextBuffer *buffer;
9736         GtkTextMark *mark;
9737         GtkTextIter ins;
9738
9739         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9740
9741         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9742         mark = gtk_text_buffer_get_insert(buffer);
9743         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9744         gtk_text_iter_set_line_offset(&ins, 0);
9745         gtk_text_buffer_place_cursor(buffer, &ins);
9746 }
9747
9748 static void textview_move_forward_character (GtkTextView *text)
9749 {
9750         GtkTextBuffer *buffer;
9751         GtkTextMark *mark;
9752         GtkTextIter ins;
9753
9754         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9755
9756         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9757         mark = gtk_text_buffer_get_insert(buffer);
9758         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9759         if (gtk_text_iter_forward_cursor_position(&ins))
9760                 gtk_text_buffer_place_cursor(buffer, &ins);
9761 }
9762
9763 static void textview_move_backward_character (GtkTextView *text)
9764 {
9765         GtkTextBuffer *buffer;
9766         GtkTextMark *mark;
9767         GtkTextIter ins;
9768
9769         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9770
9771         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9772         mark = gtk_text_buffer_get_insert(buffer);
9773         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9774         if (gtk_text_iter_backward_cursor_position(&ins))
9775                 gtk_text_buffer_place_cursor(buffer, &ins);
9776 }
9777
9778 static void textview_move_forward_word (GtkTextView *text)
9779 {
9780         GtkTextBuffer *buffer;
9781         GtkTextMark *mark;
9782         GtkTextIter ins;
9783         gint count;
9784
9785         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9786
9787         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9788         mark = gtk_text_buffer_get_insert(buffer);
9789         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9790         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9791         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9792                 gtk_text_iter_backward_word_start(&ins);
9793                 gtk_text_buffer_place_cursor(buffer, &ins);
9794         }
9795 }
9796
9797 static void textview_move_backward_word (GtkTextView *text)
9798 {
9799         GtkTextBuffer *buffer;
9800         GtkTextMark *mark;
9801         GtkTextIter ins;
9802         gint count;
9803
9804         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9805
9806         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9807         mark = gtk_text_buffer_get_insert(buffer);
9808         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9809         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9810         if (gtk_text_iter_backward_word_starts(&ins, 1))
9811                 gtk_text_buffer_place_cursor(buffer, &ins);
9812 }
9813
9814 static void textview_move_end_of_line (GtkTextView *text)
9815 {
9816         GtkTextBuffer *buffer;
9817         GtkTextMark *mark;
9818         GtkTextIter ins;
9819
9820         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9821
9822         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9823         mark = gtk_text_buffer_get_insert(buffer);
9824         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9825         if (gtk_text_iter_forward_to_line_end(&ins))
9826                 gtk_text_buffer_place_cursor(buffer, &ins);
9827 }
9828
9829 static void textview_move_next_line (GtkTextView *text)
9830 {
9831         GtkTextBuffer *buffer;
9832         GtkTextMark *mark;
9833         GtkTextIter ins;
9834         gint offset;
9835
9836         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9837
9838         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9839         mark = gtk_text_buffer_get_insert(buffer);
9840         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9841         offset = gtk_text_iter_get_line_offset(&ins);
9842         if (gtk_text_iter_forward_line(&ins)) {
9843                 gtk_text_iter_set_line_offset(&ins, offset);
9844                 gtk_text_buffer_place_cursor(buffer, &ins);
9845         }
9846 }
9847
9848 static void textview_move_previous_line (GtkTextView *text)
9849 {
9850         GtkTextBuffer *buffer;
9851         GtkTextMark *mark;
9852         GtkTextIter ins;
9853         gint offset;
9854
9855         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9856
9857         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9858         mark = gtk_text_buffer_get_insert(buffer);
9859         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9860         offset = gtk_text_iter_get_line_offset(&ins);
9861         if (gtk_text_iter_backward_line(&ins)) {
9862                 gtk_text_iter_set_line_offset(&ins, offset);
9863                 gtk_text_buffer_place_cursor(buffer, &ins);
9864         }
9865 }
9866
9867 static void textview_delete_forward_character (GtkTextView *text)
9868 {
9869         GtkTextBuffer *buffer;
9870         GtkTextMark *mark;
9871         GtkTextIter ins, end_iter;
9872
9873         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9874
9875         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9876         mark = gtk_text_buffer_get_insert(buffer);
9877         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9878         end_iter = ins;
9879         if (gtk_text_iter_forward_char(&end_iter)) {
9880                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9881         }
9882 }
9883
9884 static void textview_delete_backward_character (GtkTextView *text)
9885 {
9886         GtkTextBuffer *buffer;
9887         GtkTextMark *mark;
9888         GtkTextIter ins, end_iter;
9889
9890         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9891
9892         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9893         mark = gtk_text_buffer_get_insert(buffer);
9894         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9895         end_iter = ins;
9896         if (gtk_text_iter_backward_char(&end_iter)) {
9897                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9898         }
9899 }
9900
9901 static void textview_delete_forward_word (GtkTextView *text)
9902 {
9903         GtkTextBuffer *buffer;
9904         GtkTextMark *mark;
9905         GtkTextIter ins, end_iter;
9906
9907         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9908
9909         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9910         mark = gtk_text_buffer_get_insert(buffer);
9911         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9912         end_iter = ins;
9913         if (gtk_text_iter_forward_word_end(&end_iter)) {
9914                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9915         }
9916 }
9917
9918 static void textview_delete_backward_word (GtkTextView *text)
9919 {
9920         GtkTextBuffer *buffer;
9921         GtkTextMark *mark;
9922         GtkTextIter ins, end_iter;
9923
9924         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9925
9926         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9927         mark = gtk_text_buffer_get_insert(buffer);
9928         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9929         end_iter = ins;
9930         if (gtk_text_iter_backward_word_start(&end_iter)) {
9931                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9932         }
9933 }
9934
9935 static void textview_delete_line (GtkTextView *text)
9936 {
9937         GtkTextBuffer *buffer;
9938         GtkTextMark *mark;
9939         GtkTextIter ins, start_iter, end_iter;
9940
9941         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9942
9943         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9944         mark = gtk_text_buffer_get_insert(buffer);
9945         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9946
9947         start_iter = ins;
9948         gtk_text_iter_set_line_offset(&start_iter, 0);
9949
9950         end_iter = ins;
9951         if (gtk_text_iter_ends_line(&end_iter)){
9952                 if (!gtk_text_iter_forward_char(&end_iter))
9953                         gtk_text_iter_backward_char(&start_iter);
9954         }
9955         else 
9956                 gtk_text_iter_forward_to_line_end(&end_iter);
9957         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9958 }
9959
9960 static void textview_delete_to_line_end (GtkTextView *text)
9961 {
9962         GtkTextBuffer *buffer;
9963         GtkTextMark *mark;
9964         GtkTextIter ins, end_iter;
9965
9966         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
9967
9968         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9969         mark = gtk_text_buffer_get_insert(buffer);
9970         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9971         end_iter = ins;
9972         if (gtk_text_iter_ends_line(&end_iter))
9973                 gtk_text_iter_forward_char(&end_iter);
9974         else
9975                 gtk_text_iter_forward_to_line_end(&end_iter);
9976         gtk_text_buffer_delete(buffer, &ins, &end_iter);
9977 }
9978
9979 #define DO_ACTION(name, act) {                                          \
9980         if(!strcmp(name, a_name)) {                                     \
9981                 return act;                                             \
9982         }                                                               \
9983 }
9984 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9985 {
9986         const gchar *a_name = gtk_action_get_name(action);
9987         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9988         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9989         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9990         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9991         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9992         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9993         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9994         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9995         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9996         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9997         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9998         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9999         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10000         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10001         return -1;
10002 }
10003
10004 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10005 {
10006         Compose *compose = (Compose *)data;
10007         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10008         ComposeCallAdvancedAction action = -1;
10009         
10010         action = compose_call_advanced_action_from_path(gaction);
10011
10012         static struct {
10013                 void (*do_action) (GtkTextView *text);
10014         } action_table[] = {
10015                 {textview_move_beginning_of_line},
10016                 {textview_move_forward_character},
10017                 {textview_move_backward_character},
10018                 {textview_move_forward_word},
10019                 {textview_move_backward_word},
10020                 {textview_move_end_of_line},
10021                 {textview_move_next_line},
10022                 {textview_move_previous_line},
10023                 {textview_delete_forward_character},
10024                 {textview_delete_backward_character},
10025                 {textview_delete_forward_word},
10026                 {textview_delete_backward_word},
10027                 {textview_delete_line},
10028                 {textview_delete_to_line_end}
10029         };
10030
10031         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
10032
10033         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10034             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10035                 if (action_table[action].do_action)
10036                         action_table[action].do_action(text);
10037                 else
10038                         g_warning("Not implemented yet.");
10039         }
10040 }
10041
10042 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10043 {
10044         gchar *str = NULL;
10045         
10046         if (GTK_IS_EDITABLE(widget)) {
10047                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10048                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10049                         strlen(str));
10050                 g_free(str);
10051                 if (widget->parent && widget->parent->parent
10052                  && widget->parent->parent->parent) {
10053                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10054                                 gint y = widget->allocation.y;
10055                                 gint height = widget->allocation.height;
10056                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10057                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10058
10059                                 if (y < (int)shown->value) {
10060                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10061                                 }
10062                                 if (y + height > (int)shown->value + (int)shown->page_size) {
10063                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10064                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10065                                                         y + height - (int)shown->page_size - 1);
10066                                         } else {
10067                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10068                                                         (int)shown->upper - (int)shown->page_size - 1);
10069                                         }
10070                                 }
10071                         }
10072                 }
10073         }
10074
10075         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10076                 compose->focused_editable = widget;
10077         
10078 #ifdef GENERIC_UMPC
10079         if (GTK_IS_TEXT_VIEW(widget) 
10080             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10081                 g_object_ref(compose->notebook);
10082                 g_object_ref(compose->edit_vbox);
10083                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10084                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10085                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10086                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10087                 g_object_unref(compose->notebook);
10088                 g_object_unref(compose->edit_vbox);
10089                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10090                                         G_CALLBACK(compose_grab_focus_cb),
10091                                         compose);
10092                 gtk_widget_grab_focus(widget);
10093                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10094                                         G_CALLBACK(compose_grab_focus_cb),
10095                                         compose);
10096         } else if (!GTK_IS_TEXT_VIEW(widget) 
10097                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10098                 g_object_ref(compose->notebook);
10099                 g_object_ref(compose->edit_vbox);
10100                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10101                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10102                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10103                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10104                 g_object_unref(compose->notebook);
10105                 g_object_unref(compose->edit_vbox);
10106                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10107                                         G_CALLBACK(compose_grab_focus_cb),
10108                                         compose);
10109                 gtk_widget_grab_focus(widget);
10110                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10111                                         G_CALLBACK(compose_grab_focus_cb),
10112                                         compose);
10113         }
10114 #endif
10115 }
10116
10117 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10118 {
10119         compose->modified = TRUE;
10120 //      compose_beautify_paragraph(compose, NULL, TRUE);
10121 #ifndef GENERIC_UMPC
10122         compose_set_title(compose);
10123 #endif
10124 }
10125
10126 static void compose_wrap_cb(GtkAction *action, gpointer data)
10127 {
10128         Compose *compose = (Compose *)data;
10129         compose_beautify_paragraph(compose, NULL, TRUE);
10130 }
10131
10132 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10133 {
10134         Compose *compose = (Compose *)data;
10135         compose_wrap_all_full(compose, TRUE);
10136 }
10137
10138 static void compose_find_cb(GtkAction *action, gpointer data)
10139 {
10140         Compose *compose = (Compose *)data;
10141
10142         message_search_compose(compose);
10143 }
10144
10145 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10146                                          gpointer        data)
10147 {
10148         Compose *compose = (Compose *)data;
10149         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10150         if (compose->autowrap)
10151                 compose_wrap_all_full(compose, TRUE);
10152         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10153 }
10154
10155 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10156                                          gpointer        data)
10157 {
10158         Compose *compose = (Compose *)data;
10159         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10160 }
10161
10162 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10163 {
10164         Compose *compose = (Compose *)data;
10165
10166         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10167 }
10168
10169 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10170 {
10171         Compose *compose = (Compose *)data;
10172
10173         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10174 }
10175
10176 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10177 {
10178         g_free(compose->privacy_system);
10179
10180         compose->privacy_system = g_strdup(account->default_privacy_system);
10181         compose_update_privacy_system_menu_item(compose, warn);
10182 }
10183
10184 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10185 {
10186         Compose *compose = (Compose *)data;
10187
10188         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10189                 gtk_widget_show(compose->ruler_hbox);
10190                 prefs_common.show_ruler = TRUE;
10191         } else {
10192                 gtk_widget_hide(compose->ruler_hbox);
10193                 gtk_widget_queue_resize(compose->edit_vbox);
10194                 prefs_common.show_ruler = FALSE;
10195         }
10196 }
10197
10198 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10199                                              GdkDragContext     *context,
10200                                              gint                x,
10201                                              gint                y,
10202                                              GtkSelectionData   *data,
10203                                              guint               info,
10204                                              guint               time,
10205                                              gpointer            user_data)
10206 {
10207         Compose *compose = (Compose *)user_data;
10208         GList *list, *tmp;
10209
10210         if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10211 #ifdef G_OS_WIN32
10212          || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10213 #endif
10214            ) && gtk_drag_get_source_widget(context) != 
10215                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10216                 list = uri_list_extract_filenames((const gchar *)data->data);
10217                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10218                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10219                         compose_attach_append
10220                                 (compose, (const gchar *)tmp->data,
10221                                  utf8_filename, NULL);
10222                         g_free(utf8_filename);
10223                 }
10224                 if (list) compose_changed_cb(NULL, compose);
10225                 list_free_strings(list);
10226                 g_list_free(list);
10227         } else if (gtk_drag_get_source_widget(context) 
10228                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10229                 /* comes from our summaryview */
10230                 SummaryView * summaryview = NULL;
10231                 GSList * list = NULL, *cur = NULL;
10232                 
10233                 if (mainwindow_get_mainwindow())
10234                         summaryview = mainwindow_get_mainwindow()->summaryview;
10235                 
10236                 if (summaryview)
10237                         list = summary_get_selected_msg_list(summaryview);
10238                 
10239                 for (cur = list; cur; cur = cur->next) {
10240                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10241                         gchar *file = NULL;
10242                         if (msginfo)
10243                                 file = procmsg_get_message_file_full(msginfo, 
10244                                         TRUE, TRUE);
10245                         if (file) {
10246                                 compose_attach_append(compose, (const gchar *)file, 
10247                                         (const gchar *)file, "message/rfc822");
10248                                 g_free(file);
10249                         }
10250                 }
10251                 g_slist_free(list);
10252         }
10253 }
10254
10255 static gboolean compose_drag_drop(GtkWidget *widget,
10256                                   GdkDragContext *drag_context,
10257                                   gint x, gint y,
10258                                   guint time, gpointer user_data)
10259 {
10260         /* not handling this signal makes compose_insert_drag_received_cb
10261          * called twice */
10262         return TRUE;                                     
10263 }
10264
10265 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10266                                              GdkDragContext     *drag_context,
10267                                              gint                x,
10268                                              gint                y,
10269                                              GtkSelectionData   *data,
10270                                              guint               info,
10271                                              guint               time,
10272                                              gpointer            user_data)
10273 {
10274         Compose *compose = (Compose *)user_data;
10275         GList *list, *tmp;
10276
10277         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10278          * does not work */
10279 #ifndef G_OS_WIN32
10280         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10281 #else
10282         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10283 #endif
10284                 AlertValue val = G_ALERTDEFAULT;
10285
10286                 list = uri_list_extract_filenames((const gchar *)data->data);
10287                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10288                         /* Assume a list of no files, and data has ://, is a remote link */
10289                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10290                         gchar *tmpfile = get_tmp_file();
10291                         str_write_to_file(tmpdata, tmpfile);
10292                         g_free(tmpdata);  
10293                         compose_insert_file(compose, tmpfile);
10294                         claws_unlink(tmpfile);
10295                         g_free(tmpfile);
10296                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10297                         compose_beautify_paragraph(compose, NULL, TRUE);
10298                         return;
10299                 }
10300                 switch (prefs_common.compose_dnd_mode) {
10301                         case COMPOSE_DND_ASK:
10302                                 val = alertpanel_full(_("Insert or attach?"),
10303                                          _("Do you want to insert the contents of the file(s) "
10304                                            "into the message body, or attach it to the email?"),
10305                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10306                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10307                                 break;
10308                         case COMPOSE_DND_INSERT:
10309                                 val = G_ALERTALTERNATE;
10310                                 break;
10311                         case COMPOSE_DND_ATTACH:
10312                                 val = G_ALERTOTHER;
10313                                 break;
10314                         default:
10315                                 /* unexpected case */
10316                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10317                 }
10318
10319                 if (val & G_ALERTDISABLE) {
10320                         val &= ~G_ALERTDISABLE;
10321                         /* remember what action to perform by default, only if we don't click Cancel */
10322                         if (val == G_ALERTALTERNATE)
10323                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10324                         else if (val == G_ALERTOTHER)
10325                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10326                 }
10327
10328                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10329                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10330                         list_free_strings(list);
10331                         g_list_free(list);
10332                         return;
10333                 } else if (val == G_ALERTOTHER) {
10334                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10335                         list_free_strings(list);
10336                         g_list_free(list);
10337                         return;
10338                 } 
10339
10340                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10341                         compose_insert_file(compose, (const gchar *)tmp->data);
10342                 }
10343                 list_free_strings(list);
10344                 g_list_free(list);
10345                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10346                 return;
10347         } else {
10348 #if GTK_CHECK_VERSION(2, 8, 0)
10349                 /* do nothing, handled by GTK */
10350 #else
10351                 gchar *tmpfile = get_tmp_file();
10352                 str_write_to_file((const gchar *)data->data, tmpfile);
10353                 compose_insert_file(compose, tmpfile);
10354                 claws_unlink(tmpfile);
10355                 g_free(tmpfile);
10356                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10357 #endif
10358                 return;
10359         }
10360         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10361 }
10362
10363 static void compose_header_drag_received_cb (GtkWidget          *widget,
10364                                              GdkDragContext     *drag_context,
10365                                              gint                x,
10366                                              gint                y,
10367                                              GtkSelectionData   *data,
10368                                              guint               info,
10369                                              guint               time,
10370                                              gpointer            user_data)
10371 {
10372         GtkEditable *entry = (GtkEditable *)user_data;
10373         gchar *email = (gchar *)data->data;
10374
10375         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10376          * does not work */
10377
10378         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10379                 gchar *decoded=g_new(gchar, strlen(email));
10380                 int start = 0;
10381
10382                 email += strlen("mailto:");
10383                 decode_uri(decoded, email); /* will fit */
10384                 gtk_editable_delete_text(entry, 0, -1);
10385                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10386                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10387                 g_free(decoded);
10388                 return;
10389         }
10390         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10391 }
10392
10393 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10394 {
10395         Compose *compose = (Compose *)data;
10396
10397         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10398                 compose->return_receipt = TRUE;
10399         else
10400                 compose->return_receipt = FALSE;
10401 }
10402
10403 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10404 {
10405         Compose *compose = (Compose *)data;
10406
10407         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10408                 compose->remove_references = TRUE;
10409         else
10410                 compose->remove_references = FALSE;
10411 }
10412
10413 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10414                                         ComposeHeaderEntry *headerentry)
10415 {
10416         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10417         return FALSE;
10418 }
10419
10420 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10421                                             GdkEventKey *event,
10422                                             ComposeHeaderEntry *headerentry)
10423 {
10424         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10425             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10426             !(event->state & GDK_MODIFIER_MASK) &&
10427             (event->keyval == GDK_BackSpace) &&
10428             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10429                 gtk_container_remove
10430                         (GTK_CONTAINER(headerentry->compose->header_table),
10431                          headerentry->combo);
10432                 gtk_container_remove
10433                         (GTK_CONTAINER(headerentry->compose->header_table),
10434                          headerentry->entry);
10435                 headerentry->compose->header_list =
10436                         g_slist_remove(headerentry->compose->header_list,
10437                                        headerentry);
10438                 g_free(headerentry);
10439         } else  if (event->keyval == GDK_Tab) {
10440                 if (headerentry->compose->header_last == headerentry) {
10441                         /* Override default next focus, and give it to subject_entry
10442                          * instead of notebook tabs
10443                          */
10444                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10445                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10446                         return TRUE;
10447                 }
10448         }
10449         return FALSE;
10450 }
10451
10452 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10453                                     ComposeHeaderEntry *headerentry)
10454 {
10455         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10456                 compose_create_header_entry(headerentry->compose);
10457                 g_signal_handlers_disconnect_matched
10458                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10459                          0, 0, NULL, NULL, headerentry);
10460                 
10461                 /* Automatically scroll down */
10462                 compose_show_first_last_header(headerentry->compose, FALSE);
10463                 
10464         }
10465         return FALSE;
10466 }
10467
10468 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10469 {
10470         GtkAdjustment *vadj;
10471
10472         cm_return_if_fail(compose);
10473         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10474         cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10475
10476         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10477         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10478         gtk_adjustment_changed(vadj);
10479 }
10480
10481 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10482                           const gchar *text, gint len, Compose *compose)
10483 {
10484         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10485                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10486         GtkTextMark *mark;
10487
10488         cm_return_if_fail(text != NULL);
10489
10490         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10491                                         G_CALLBACK(text_inserted),
10492                                         compose);
10493         if (paste_as_quotation) {
10494                 gchar *new_text;
10495                 const gchar *qmark;
10496                 guint pos = 0;
10497                 GtkTextIter start_iter;
10498
10499                 if (len < 0)
10500                         len = strlen(text);
10501
10502                 new_text = g_strndup(text, len);
10503
10504                 qmark = compose_quote_char_from_context(compose);
10505
10506                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10507                 gtk_text_buffer_place_cursor(buffer, iter);
10508
10509                 pos = gtk_text_iter_get_offset(iter);
10510
10511                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10512                                                   _("Quote format error at line %d."));
10513                 quote_fmt_reset_vartable();
10514                 g_free(new_text);
10515                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10516                                   GINT_TO_POINTER(paste_as_quotation - 1));
10517                                   
10518                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10519                 gtk_text_buffer_place_cursor(buffer, iter);
10520                 gtk_text_buffer_delete_mark(buffer, mark);
10521
10522                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10523                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10524                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10525                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10526                 gtk_text_buffer_delete_mark(buffer, mark);
10527         } else {
10528                 if (strcmp(text, "\n") || compose->automatic_break
10529                 || gtk_text_iter_starts_line(iter)) {
10530                         GtkTextIter before_ins;
10531                         gtk_text_buffer_insert(buffer, iter, text, len);
10532                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10533                                 before_ins = *iter; 
10534                                 gtk_text_iter_backward_chars(&before_ins, len);
10535                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10536                         }
10537                 } else {
10538                         /* check if the preceding is just whitespace or quote */
10539                         GtkTextIter start_line;
10540                         gchar *tmp = NULL, *quote = NULL;
10541                         gint quote_len = 0, is_normal = 0;
10542                         start_line = *iter;
10543                         gtk_text_iter_set_line_offset(&start_line, 0); 
10544                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10545                         g_strstrip(tmp);
10546
10547                         if (*tmp == '\0') {
10548                                 is_normal = 1;
10549                         } else {
10550                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10551                                 if (quote)
10552                                         is_normal = 1;
10553                                 g_free(quote);
10554                         }
10555                         g_free(tmp);
10556                         
10557                         if (is_normal) {
10558                                 gtk_text_buffer_insert(buffer, iter, text, len);
10559                         } else {
10560                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10561                                         iter, text, len, "no_join", NULL);
10562                         }
10563                 }
10564         }
10565         
10566         if (!paste_as_quotation) {
10567                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10568                 compose_beautify_paragraph(compose, iter, FALSE);
10569                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10570                 gtk_text_buffer_delete_mark(buffer, mark);
10571         }
10572
10573         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10574                                           G_CALLBACK(text_inserted),
10575                                           compose);
10576         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10577
10578         if (prefs_common.autosave && 
10579             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10580             compose->draft_timeout_tag != -2 /* disabled while loading */)
10581                 compose->draft_timeout_tag = g_timeout_add
10582                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10583 }
10584 static gint compose_defer_auto_save_draft(Compose *compose)
10585 {
10586         compose->draft_timeout_tag = -1;
10587         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10588         return FALSE;
10589 }
10590
10591 #if USE_ENCHANT
10592 static void compose_check_all(GtkAction *action, gpointer data)
10593 {
10594         Compose *compose = (Compose *)data;
10595         if (!compose->gtkaspell)
10596                 return;
10597                 
10598         if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10599                 claws_spell_entry_check_all(
10600                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
10601         else
10602                 gtkaspell_check_all(compose->gtkaspell);
10603 }
10604
10605 static void compose_highlight_all(GtkAction *action, gpointer data)
10606 {
10607         Compose *compose = (Compose *)data;
10608         if (compose->gtkaspell) {
10609                 claws_spell_entry_recheck_all(
10610                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10611                 gtkaspell_highlight_all(compose->gtkaspell);
10612         }
10613 }
10614
10615 static void compose_check_backwards(GtkAction *action, gpointer data)
10616 {
10617         Compose *compose = (Compose *)data;
10618         if (!compose->gtkaspell) {
10619                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10620                 return;
10621         }
10622
10623         if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10624                 claws_spell_entry_check_backwards(
10625                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10626         else
10627                 gtkaspell_check_backwards(compose->gtkaspell);
10628 }
10629
10630 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10631 {
10632         Compose *compose = (Compose *)data;
10633         if (!compose->gtkaspell) {
10634                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10635                 return;
10636         }
10637
10638         if (GTK_WIDGET_HAS_FOCUS(compose->subject_entry))
10639                 claws_spell_entry_check_forwards_go(
10640                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10641         else
10642                 gtkaspell_check_forwards_go(compose->gtkaspell);
10643 }
10644 #endif
10645
10646 /*!
10647  *\brief        Guess originating forward account from MsgInfo and several 
10648  *              "common preference" settings. Return NULL if no guess. 
10649  */
10650 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10651 {
10652         PrefsAccount *account = NULL;
10653         
10654         cm_return_val_if_fail(msginfo, NULL);
10655         cm_return_val_if_fail(msginfo->folder, NULL);
10656         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10657
10658         if (msginfo->folder->prefs->enable_default_account)
10659                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10660                 
10661         if (!account) 
10662                 account = msginfo->folder->folder->account;
10663                 
10664         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10665                 gchar *to;
10666                 Xstrdup_a(to, msginfo->to, return NULL);
10667                 extract_address(to);
10668                 account = account_find_from_address(to, FALSE);
10669         }
10670
10671         if (!account && prefs_common.forward_account_autosel) {
10672                 gchar cc[BUFFSIZE];
10673                 if (!procheader_get_header_from_msginfo
10674                         (msginfo, cc,sizeof cc , "Cc:")) { 
10675                         gchar *buf = cc + strlen("Cc:");
10676                         extract_address(buf);
10677                         account = account_find_from_address(buf, FALSE);
10678                 }
10679         }
10680         
10681         if (!account && prefs_common.forward_account_autosel) {
10682                 gchar deliveredto[BUFFSIZE];
10683                 if (!procheader_get_header_from_msginfo
10684                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10685                         gchar *buf = deliveredto + strlen("Delivered-To:");
10686                         extract_address(buf);
10687                         account = account_find_from_address(buf, FALSE);
10688                 }
10689         }
10690         
10691         return account;
10692 }
10693
10694 gboolean compose_close(Compose *compose)
10695 {
10696         gint x, y;
10697
10698         if (!g_mutex_trylock(compose->mutex)) {
10699                 /* we have to wait for the (possibly deferred by auto-save)
10700                  * drafting to be done, before destroying the compose under
10701                  * it. */
10702                 debug_print("waiting for drafting to finish...\n");
10703                 compose_allow_user_actions(compose, FALSE);
10704                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10705                 return FALSE;
10706         }
10707         cm_return_val_if_fail(compose, FALSE);
10708         gtkut_widget_get_uposition(compose->window, &x, &y);
10709         if (!compose->batch) {
10710                 prefs_common.compose_x = x;
10711                 prefs_common.compose_y = y;
10712         }
10713         g_mutex_unlock(compose->mutex);
10714         compose_destroy(compose);
10715         return FALSE;
10716 }
10717
10718 /**
10719  * Add entry field for each address in list.
10720  * \param compose     E-Mail composition object.
10721  * \param listAddress List of (formatted) E-Mail addresses.
10722  */
10723 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10724         GList *node;
10725         gchar *addr;
10726         node = listAddress;
10727         while( node ) {
10728                 addr = ( gchar * ) node->data;
10729                 compose_entry_append( compose, addr, COMPOSE_TO );
10730                 node = g_list_next( node );
10731         }
10732 }
10733
10734 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10735                                     guint action, gboolean opening_multiple)
10736 {
10737         gchar *body = NULL;
10738         GSList *new_msglist = NULL;
10739         MsgInfo *tmp_msginfo = NULL;
10740         gboolean originally_enc = FALSE;
10741         gboolean originally_sig = FALSE;
10742         Compose *compose = NULL;
10743         gchar *s_system = NULL;
10744
10745         cm_return_if_fail(msgview != NULL);
10746
10747         cm_return_if_fail(msginfo_list != NULL);
10748
10749         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10750                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10751                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10752
10753                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10754                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10755                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10756                                                 orig_msginfo, mimeinfo);
10757                         if (tmp_msginfo != NULL) {
10758                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10759
10760                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10761                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10762                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10763
10764                                 tmp_msginfo->folder = orig_msginfo->folder;
10765                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10766                                 if (orig_msginfo->tags) {
10767                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10768                                         tmp_msginfo->folder->tags_dirty = TRUE;
10769                                 }
10770                         }
10771                 }
10772         }
10773
10774         if (!opening_multiple)
10775                 body = messageview_get_selection(msgview);
10776
10777         if (new_msglist) {
10778                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10779                 procmsg_msginfo_free(tmp_msginfo);
10780                 g_slist_free(new_msglist);
10781         } else
10782                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10783
10784         if (compose && originally_enc) {
10785                 compose_force_encryption(compose, compose->account, FALSE, s_system);
10786         }
10787
10788         if (compose && originally_sig && compose->account->default_sign_reply) {
10789                 compose_force_signing(compose, compose->account, s_system);
10790         }
10791         g_free(s_system);
10792         g_free(body);
10793 }
10794
10795 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10796                                     guint action)
10797 {
10798         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10799         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10800                 GSList *cur = msginfo_list;
10801                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10802                                                "messages. Opening the windows "
10803                                                "could take some time. Do you "
10804                                                "want to continue?"), 
10805                                                g_slist_length(msginfo_list));
10806                 if (g_slist_length(msginfo_list) > 9
10807                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10808                     != G_ALERTALTERNATE) {
10809                         g_free(msg);
10810                         return;
10811                 }
10812                 g_free(msg);
10813                 /* We'll open multiple compose windows */
10814                 /* let the WM place the next windows */
10815                 compose_force_window_origin = FALSE;
10816                 for (; cur; cur = cur->next) {
10817                         GSList tmplist;
10818                         tmplist.data = cur->data;
10819                         tmplist.next = NULL;
10820                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10821                 }
10822                 compose_force_window_origin = TRUE;
10823         } else {
10824                 /* forwarding multiple mails as attachments is done via a
10825                  * single compose window */
10826                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10827         }
10828 }
10829
10830 void compose_set_position(Compose *compose, gint pos)
10831 {
10832         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10833
10834         gtkut_text_view_set_position(text, pos);
10835 }
10836
10837 gboolean compose_search_string(Compose *compose,
10838                                 const gchar *str, gboolean case_sens)
10839 {
10840         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10841
10842         return gtkut_text_view_search_string(text, str, case_sens);
10843 }
10844
10845 gboolean compose_search_string_backward(Compose *compose,
10846                                 const gchar *str, gboolean case_sens)
10847 {
10848         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10849
10850         return gtkut_text_view_search_string_backward(text, str, case_sens);
10851 }
10852
10853 /* allocate a msginfo structure and populate its data from a compose data structure */
10854 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10855 {
10856         MsgInfo *newmsginfo;
10857         GSList *list;
10858         gchar buf[BUFFSIZE];
10859
10860         cm_return_val_if_fail( compose != NULL, NULL );
10861
10862         newmsginfo = procmsg_msginfo_new();
10863
10864         /* date is now */
10865         get_rfc822_date(buf, sizeof(buf));
10866         newmsginfo->date = g_strdup(buf);
10867
10868         /* from */
10869         if (compose->from_name) {
10870                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10871                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10872         }
10873
10874         /* subject */
10875         if (compose->subject_entry)
10876                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10877
10878         /* to, cc, reply-to, newsgroups */
10879         for (list = compose->header_list; list; list = list->next) {
10880                 gchar *header = gtk_editable_get_chars(
10881                                                                 GTK_EDITABLE(
10882                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10883                 gchar *entry = gtk_editable_get_chars(
10884                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10885
10886                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10887                         if ( newmsginfo->to == NULL ) {
10888                                 newmsginfo->to = g_strdup(entry);
10889                         } else if (entry && *entry) {
10890                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10891                                 g_free(newmsginfo->to);
10892                                 newmsginfo->to = tmp;
10893                         }
10894                 } else
10895                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10896                         if ( newmsginfo->cc == NULL ) {
10897                                 newmsginfo->cc = g_strdup(entry);
10898                         } else if (entry && *entry) {
10899                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10900                                 g_free(newmsginfo->cc);
10901                                 newmsginfo->cc = tmp;
10902                         }
10903                 } else
10904                 if ( strcasecmp(header,
10905                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10906                         if ( newmsginfo->newsgroups == NULL ) {
10907                                 newmsginfo->newsgroups = g_strdup(entry);
10908                         } else if (entry && *entry) {
10909                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10910                                 g_free(newmsginfo->newsgroups);
10911                                 newmsginfo->newsgroups = tmp;
10912                         }
10913                 }
10914
10915                 g_free(header);
10916                 g_free(entry);  
10917         }
10918
10919         /* other data is unset */
10920
10921         return newmsginfo;
10922 }
10923
10924 #ifdef USE_ENCHANT
10925 /* update compose's dictionaries from folder dict settings */
10926 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10927                                                 FolderItem *folder_item)
10928 {
10929         cm_return_if_fail(compose != NULL);
10930
10931         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10932                 FolderItemPrefs *prefs = folder_item->prefs;
10933
10934                 if (prefs->enable_default_dictionary)
10935                         gtkaspell_change_dict(compose->gtkaspell,
10936                                         prefs->default_dictionary, FALSE);
10937                 if (folder_item->prefs->enable_default_alt_dictionary)
10938                         gtkaspell_change_alt_dict(compose->gtkaspell,
10939                                         prefs->default_alt_dictionary);
10940                 if (prefs->enable_default_dictionary
10941                         || prefs->enable_default_alt_dictionary)
10942                         compose_spell_menu_changed(compose);
10943         }
10944 }
10945 #endif
10946
10947 /*
10948  * End of Source.
10949  */