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