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