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