2010-05-14 [colin] 3.7.6cvs4
[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         
3003         /* Some quotes may not end with new line - when replying with
3004          * quote from GtkTextBuffer selection for example. Add new
3005          * line character so cursor is at the beginning of the line */
3006         if (!gtk_text_iter_starts_line(&iter)) {
3007                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
3008                 gtk_text_buffer_get_end_iter(buffer, &iter);
3009                 compose->set_cursor_pos = gtk_text_iter_get_offset(&iter);
3010         }
3011         gtk_text_buffer_place_cursor(buffer, &iter);
3012
3013         compose->autowrap = prev_autowrap;
3014         if (compose->autowrap && rewrap)
3015                 compose_wrap_all(compose);
3016
3017         goto ok;
3018
3019 error:
3020         buf = NULL;
3021 ok:
3022         SIGNAL_UNBLOCK(buffer);
3023
3024         procmsg_msginfo_free( dummyinfo );
3025
3026         return buf;
3027 }
3028
3029 /* if ml_post is of type addr@host and from is of type
3030  * addr-anything@host, return TRUE
3031  */
3032 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3033 {
3034         gchar *left_ml = NULL;
3035         gchar *right_ml = NULL;
3036         gchar *left_from = NULL;
3037         gchar *right_from = NULL;
3038         gboolean result = FALSE;
3039         
3040         if (!ml_post || !from)
3041                 return FALSE;
3042         
3043         left_ml = g_strdup(ml_post);
3044         if (strstr(left_ml, "@")) {
3045                 right_ml = strstr(left_ml, "@")+1;
3046                 *(strstr(left_ml, "@")) = '\0';
3047         }
3048         
3049         left_from = g_strdup(from);
3050         if (strstr(left_from, "@")) {
3051                 right_from = strstr(left_from, "@")+1;
3052                 *(strstr(left_from, "@")) = '\0';
3053         }
3054         
3055         if (left_ml && left_from && right_ml && right_from
3056         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3057         &&  !strcmp(right_from, right_ml)) {
3058                 result = TRUE;
3059         }
3060         g_free(left_ml);
3061         g_free(left_from);
3062         
3063         return result;
3064 }
3065
3066 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3067                                      gboolean respect_default_to)
3068 {
3069         if (!compose)
3070                 return;
3071         if (!folder || !folder->prefs)
3072                 return;
3073
3074         if (respect_default_to && folder->prefs->enable_default_to) {
3075                 compose_entry_append(compose, folder->prefs->default_to,
3076                                         COMPOSE_TO, PREF_FOLDER);
3077                 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3078         }
3079         if (folder->prefs->enable_default_cc)
3080                 compose_entry_append(compose, folder->prefs->default_cc,
3081                                         COMPOSE_CC, PREF_FOLDER);
3082         if (folder->prefs->enable_default_bcc)
3083                 compose_entry_append(compose, folder->prefs->default_bcc,
3084                                         COMPOSE_BCC, PREF_FOLDER);
3085         if (folder->prefs->enable_default_replyto)
3086                 compose_entry_append(compose, folder->prefs->default_replyto,
3087                                         COMPOSE_REPLYTO, PREF_FOLDER);
3088 }
3089
3090 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3091 {
3092         gchar *buf, *buf2;
3093         gchar *p;
3094         
3095         if (!compose || !msginfo)
3096                 return;
3097
3098         if (msginfo->subject && *msginfo->subject) {
3099                 buf = p = g_strdup(msginfo->subject);
3100                 p += subject_get_prefix_length(p);
3101                 memmove(buf, p, strlen(p) + 1);
3102
3103                 buf2 = g_strdup_printf("Re: %s", buf);
3104                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3105
3106                 g_free(buf2);
3107                 g_free(buf);
3108         } else
3109                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3110 }
3111
3112 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3113                                     gboolean to_all, gboolean to_ml,
3114                                     gboolean to_sender,
3115                                     gboolean followup_and_reply_to)
3116 {
3117         GSList *cc_list = NULL;
3118         GSList *cur;
3119         gchar *from = NULL;
3120         gchar *replyto = NULL;
3121         gchar *ac_email = NULL;
3122
3123         gboolean reply_to_ml = FALSE;
3124         gboolean default_reply_to = FALSE;
3125
3126         cm_return_if_fail(compose->account != NULL);
3127         cm_return_if_fail(msginfo != NULL);
3128
3129         reply_to_ml = to_ml && compose->ml_post;
3130
3131         default_reply_to = msginfo->folder && 
3132                 msginfo->folder->prefs->enable_default_reply_to;
3133
3134         if (compose->account->protocol != A_NNTP) {
3135                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3136
3137                 if (reply_to_ml && !default_reply_to) {
3138                         
3139                         gboolean is_subscr = is_subscription(compose->ml_post,
3140                                                              msginfo->from);
3141                         if (!is_subscr) {
3142                                 /* normal answer to ml post with a reply-to */
3143                                 compose_entry_append(compose,
3144                                            compose->ml_post,
3145                                            COMPOSE_TO, PREF_ML);
3146                                 if (compose->replyto)
3147                                         compose_entry_append(compose,
3148                                                 compose->replyto,
3149                                                 COMPOSE_CC, PREF_ML);
3150                         } else {
3151                                 /* answer to subscription confirmation */
3152                                 if (compose->replyto)
3153                                         compose_entry_append(compose,
3154                                                 compose->replyto,
3155                                                 COMPOSE_TO, PREF_ML);
3156                                 else if (msginfo->from)
3157                                         compose_entry_append(compose,
3158                                                 msginfo->from,
3159                                                 COMPOSE_TO, PREF_ML);
3160                         }
3161                 }
3162                 else if (!(to_all || to_sender) && default_reply_to) {
3163                         compose_entry_append(compose,
3164                             msginfo->folder->prefs->default_reply_to,
3165                             COMPOSE_TO, PREF_FOLDER);
3166                         compose_entry_mark_default_to(compose,
3167                                 msginfo->folder->prefs->default_reply_to);
3168                 } else {
3169                         gchar *tmp1 = NULL;
3170                         if (!msginfo->from)
3171                                 return;
3172                         Xstrdup_a(tmp1, msginfo->from, return);
3173                         extract_address(tmp1);
3174                         if (to_all || to_sender ||
3175                             !account_find_from_address(tmp1, FALSE))
3176                                 compose_entry_append(compose,
3177                                  (compose->replyto && !to_sender)
3178                                           ? compose->replyto :
3179                                           msginfo->from ? msginfo->from : "",
3180                                           COMPOSE_TO, PREF_NONE);
3181                         else if (!to_all && !to_sender) {
3182                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3183                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3184                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3185                                         if (compose->replyto) {
3186                                                 compose_entry_append(compose,
3187                                                         compose->replyto,
3188                                                         COMPOSE_TO, PREF_NONE);
3189                                         } else {
3190                                                 compose_entry_append(compose,
3191                                                           msginfo->from ? msginfo->from : "",
3192                                                           COMPOSE_TO, PREF_NONE);
3193                                         }
3194                                 } else {
3195                                         /* replying to own mail, use original recp */
3196                                         compose_entry_append(compose,
3197                                                   msginfo->to ? msginfo->to : "",
3198                                                   COMPOSE_TO, PREF_NONE);
3199                                         compose_entry_append(compose,
3200                                                   msginfo->cc ? msginfo->cc : "",
3201                                                   COMPOSE_CC, PREF_NONE);
3202                                 }
3203                         }
3204                 }
3205         } else {
3206                 if (to_sender || (compose->followup_to && 
3207                         !strncmp(compose->followup_to, "poster", 6)))
3208                         compose_entry_append
3209                                 (compose, 
3210                                  (compose->replyto ? compose->replyto :
3211                                         msginfo->from ? msginfo->from : ""),
3212                                  COMPOSE_TO, PREF_NONE);
3213                                  
3214                 else if (followup_and_reply_to || to_all) {
3215                         compose_entry_append
3216                                 (compose,
3217                                  (compose->replyto ? compose->replyto :
3218                                  msginfo->from ? msginfo->from : ""),
3219                                  COMPOSE_TO, PREF_NONE);                                
3220                 
3221                         compose_entry_append
3222                                 (compose,
3223                                  compose->followup_to ? compose->followup_to :
3224                                  compose->newsgroups ? compose->newsgroups : "",
3225                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3226                 } 
3227                 else 
3228                         compose_entry_append
3229                                 (compose,
3230                                  compose->followup_to ? compose->followup_to :
3231                                  compose->newsgroups ? compose->newsgroups : "",
3232                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3233         }
3234         compose_reply_set_subject(compose, msginfo);
3235
3236         if (to_ml && compose->ml_post) return;
3237         if (!to_all || compose->account->protocol == A_NNTP) return;
3238
3239         if (compose->replyto) {
3240                 Xstrdup_a(replyto, compose->replyto, return);
3241                 extract_address(replyto);
3242         }
3243         if (msginfo->from) {
3244                 Xstrdup_a(from, msginfo->from, return);
3245                 extract_address(from);
3246         }
3247
3248         if (replyto && from)
3249                 cc_list = address_list_append_with_comments(cc_list, from);
3250         if (to_all && msginfo->folder && 
3251             msginfo->folder->prefs->enable_default_reply_to)
3252                 cc_list = address_list_append_with_comments(cc_list,
3253                                 msginfo->folder->prefs->default_reply_to);
3254         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3255         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3256
3257         ac_email = g_utf8_strdown(compose->account->address, -1);
3258
3259         if (cc_list) {
3260                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3261                         gchar *addr = g_utf8_strdown(cur->data, -1);
3262                         extract_address(addr);
3263                 
3264                         if (strcmp(ac_email, addr))
3265                                 compose_entry_append(compose, (gchar *)cur->data,
3266                                                      COMPOSE_CC, PREF_NONE);
3267                         else
3268                                 debug_print("Cc address same as compose account's, ignoring\n");
3269
3270                         g_free(addr);
3271                 }
3272                 
3273                 slist_free_strings(cc_list);
3274                 g_slist_free(cc_list);
3275         }
3276         
3277         g_free(ac_email);
3278 }
3279
3280 #define SET_ENTRY(entry, str) \
3281 { \
3282         if (str && *str) \
3283                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3284 }
3285
3286 #define SET_ADDRESS(type, str) \
3287 { \
3288         if (str && *str) \
3289                 compose_entry_append(compose, str, type, PREF_NONE); \
3290 }
3291
3292 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3293 {
3294         cm_return_if_fail(msginfo != NULL);
3295
3296         SET_ENTRY(subject_entry, msginfo->subject);
3297         SET_ENTRY(from_name, msginfo->from);
3298         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3299         SET_ADDRESS(COMPOSE_CC, compose->cc);
3300         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3301         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3302         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3303         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3304
3305         compose_update_priority_menu_item(compose);
3306         compose_update_privacy_system_menu_item(compose, FALSE);
3307         compose_show_first_last_header(compose, TRUE);
3308 }
3309
3310 #undef SET_ENTRY
3311 #undef SET_ADDRESS
3312
3313 static void compose_insert_sig(Compose *compose, gboolean replace)
3314 {
3315         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3316         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3317         GtkTextMark *mark;
3318         GtkTextIter iter, iter_end;
3319         gint cur_pos, ins_pos;
3320         gboolean prev_autowrap;
3321         gboolean found = FALSE;
3322         gboolean exists = FALSE;
3323         
3324         cm_return_if_fail(compose->account != NULL);
3325
3326         BLOCK_WRAP();
3327
3328         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3329                                         G_CALLBACK(compose_changed_cb),
3330                                         compose);
3331         
3332         mark = gtk_text_buffer_get_insert(buffer);
3333         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3334         cur_pos = gtk_text_iter_get_offset (&iter);
3335         ins_pos = cur_pos;
3336
3337         gtk_text_buffer_get_end_iter(buffer, &iter);
3338
3339         exists = (compose->sig_str != NULL);
3340
3341         if (replace) {
3342                 GtkTextIter first_iter, start_iter, end_iter;
3343
3344                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3345
3346                 if (!exists || compose->sig_str[0] == '\0')
3347                         found = FALSE;
3348                 else
3349                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3350                                         compose->signature_tag);
3351
3352                 if (found) {
3353                         /* include previous \n\n */
3354                         gtk_text_iter_backward_chars(&first_iter, 1);
3355                         start_iter = first_iter;
3356                         end_iter = first_iter;
3357                         /* skip re-start */
3358                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3359                                         compose->signature_tag);
3360                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3361                                         compose->signature_tag);
3362                         if (found) {
3363                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3364                                 iter = start_iter;
3365                         }
3366                 } 
3367         } 
3368
3369         g_free(compose->sig_str);
3370         compose->sig_str = account_get_signature_str(compose->account);
3371
3372         cur_pos = gtk_text_iter_get_offset(&iter);
3373
3374         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3375                 g_free(compose->sig_str);
3376                 compose->sig_str = NULL;
3377         } else {
3378                 if (compose->sig_inserted == FALSE)
3379                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3380                 compose->sig_inserted = TRUE;
3381
3382                 cur_pos = gtk_text_iter_get_offset(&iter);
3383                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3384                 /* remove \n\n */
3385                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3386                 gtk_text_iter_forward_chars(&iter, 1);
3387                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3388                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3389
3390                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3391                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3392         }
3393
3394         /* put the cursor where it should be 
3395          * either where the quote_fmt says, either where it was */
3396         if (compose->set_cursor_pos < 0)
3397                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3398         else
3399                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3400                         compose->set_cursor_pos);
3401         
3402         compose->set_cursor_pos = -1;
3403         gtk_text_buffer_place_cursor(buffer, &iter);
3404         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3405                                         G_CALLBACK(compose_changed_cb),
3406                                         compose);
3407                 
3408         UNBLOCK_WRAP();
3409 }
3410
3411 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3412 {
3413         GtkTextView *text;
3414         GtkTextBuffer *buffer;
3415         GtkTextMark *mark;
3416         GtkTextIter iter;
3417         const gchar *cur_encoding;
3418         gchar buf[BUFFSIZE];
3419         gint len;
3420         FILE *fp;
3421         gboolean prev_autowrap;
3422         gboolean badtxt = FALSE;
3423         struct stat file_stat;
3424         int ret;
3425
3426         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3427
3428         /* get the size of the file we are about to insert */
3429         ret = g_stat(file, &file_stat);
3430         if (ret != 0) {
3431                 gchar *shortfile = g_path_get_basename(file);
3432                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3433                 g_free(shortfile);
3434                 return COMPOSE_INSERT_NO_FILE;
3435         } else if (prefs_common.warn_large_insert == TRUE) {
3436
3437                 /* ask user for confirmation if the file is large */
3438                 if (prefs_common.warn_large_insert_size < 0 ||
3439                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3440                         AlertValue aval;
3441                         gchar *msg;
3442
3443                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3444                                                 "in the message body. Are you sure you want to do that?"),
3445                                                 to_human_readable(file_stat.st_size));
3446                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3447                                         _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3448                         g_free(msg);
3449
3450                         /* do we ask for confirmation next time? */
3451                         if (aval & G_ALERTDISABLE) {
3452                                 /* no confirmation next time, disable feature in preferences */
3453                                 aval &= ~G_ALERTDISABLE;
3454                                 prefs_common.warn_large_insert = FALSE;
3455                         }
3456
3457                         /* abort file insertion if user canceled action */
3458                         if (aval != G_ALERTALTERNATE) {
3459                                 return COMPOSE_INSERT_NO_FILE;
3460                         }
3461                 }
3462         }
3463
3464
3465         if ((fp = g_fopen(file, "rb")) == NULL) {
3466                 FILE_OP_ERROR(file, "fopen");
3467                 return COMPOSE_INSERT_READ_ERROR;
3468         }
3469
3470         prev_autowrap = compose->autowrap;
3471         compose->autowrap = FALSE;
3472
3473         text = GTK_TEXT_VIEW(compose->text);
3474         buffer = gtk_text_view_get_buffer(text);
3475         mark = gtk_text_buffer_get_insert(buffer);
3476         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3477
3478         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3479                                         G_CALLBACK(text_inserted),
3480                                         compose);
3481
3482         cur_encoding = conv_get_locale_charset_str_no_utf8();
3483
3484         while (fgets(buf, sizeof(buf), fp) != NULL) {
3485                 gchar *str;
3486
3487                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3488                         str = g_strdup(buf);
3489                 else
3490                         str = conv_codeset_strdup
3491                                 (buf, cur_encoding, CS_INTERNAL);
3492                 if (!str) continue;
3493
3494                 /* strip <CR> if DOS/Windows file,
3495                    replace <CR> with <LF> if Macintosh file. */
3496                 strcrchomp(str);
3497                 len = strlen(str);
3498                 if (len > 0 && str[len - 1] != '\n') {
3499                         while (--len >= 0)
3500                                 if (str[len] == '\r') str[len] = '\n';
3501                 }
3502
3503                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3504                 g_free(str);
3505         }
3506
3507         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3508                                           G_CALLBACK(text_inserted),
3509                                           compose);
3510         compose->autowrap = prev_autowrap;
3511         if (compose->autowrap)
3512                 compose_wrap_all(compose);
3513
3514         fclose(fp);
3515
3516         if (badtxt)
3517                 return COMPOSE_INSERT_INVALID_CHARACTER;
3518         else 
3519                 return COMPOSE_INSERT_SUCCESS;
3520 }
3521
3522 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3523                                   const gchar *filename,
3524                                   const gchar *content_type)
3525 {
3526         AttachInfo *ainfo;
3527         GtkTreeIter iter;
3528         FILE *fp;
3529         off_t size;
3530         GAuto *auto_ainfo;
3531         gchar *size_text;
3532         GtkListStore *store;
3533         gchar *name;
3534         gboolean has_binary = FALSE;
3535
3536         if (!is_file_exist(file)) {
3537                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3538                 gboolean result = FALSE;
3539                 if (file_from_uri && is_file_exist(file_from_uri)) {
3540                         result = compose_attach_append(
3541                                                 compose, file_from_uri,
3542                                                 filename,
3543                                                 content_type);
3544                 }
3545                 g_free(file_from_uri);
3546                 if (result)
3547                         return TRUE;
3548                 alertpanel_error("File %s doesn't exist\n", filename);
3549                 return FALSE;
3550         }
3551         if ((size = get_file_size(file)) < 0) {
3552                 alertpanel_error("Can't get file size of %s\n", filename);
3553                 return FALSE;
3554         }
3555         if (size == 0) {
3556                 alertpanel_error(_("File %s is empty."), filename);
3557                 return FALSE;
3558         }
3559         if ((fp = g_fopen(file, "rb")) == NULL) {
3560                 alertpanel_error(_("Can't read %s."), filename);
3561                 return FALSE;
3562         }
3563         fclose(fp);
3564
3565         ainfo = g_new0(AttachInfo, 1);
3566         auto_ainfo = g_auto_pointer_new_with_free
3567                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3568         ainfo->file = g_strdup(file);
3569
3570         if (content_type) {
3571                 ainfo->content_type = g_strdup(content_type);
3572                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3573                         MsgInfo *msginfo;
3574                         MsgFlags flags = {0, 0};
3575
3576                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3577                                 ainfo->encoding = ENC_7BIT;
3578                         else
3579                                 ainfo->encoding = ENC_8BIT;
3580
3581                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3582                         if (msginfo && msginfo->subject)
3583                                 name = g_strdup(msginfo->subject);
3584                         else
3585                                 name = g_path_get_basename(filename ? filename : file);
3586
3587                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3588
3589                         procmsg_msginfo_free(msginfo);
3590                 } else {
3591                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3592                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3593                         else
3594                                 ainfo->encoding = ENC_BASE64;
3595                         name = g_path_get_basename(filename ? filename : file);
3596                         ainfo->name = g_strdup(name);
3597                 }
3598                 g_free(name);
3599         } else {
3600                 ainfo->content_type = procmime_get_mime_type(file);
3601                 if (!ainfo->content_type) {
3602                         ainfo->content_type =
3603                                 g_strdup("application/octet-stream");
3604                         ainfo->encoding = ENC_BASE64;
3605                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3606                         ainfo->encoding =
3607                                 procmime_get_encoding_for_text_file(file, &has_binary);
3608                 else
3609                         ainfo->encoding = ENC_BASE64;
3610                 name = g_path_get_basename(filename ? filename : file);
3611                 ainfo->name = g_strdup(name);   
3612                 g_free(name);
3613         }
3614
3615         if (ainfo->name != NULL
3616         &&  !strcmp(ainfo->name, ".")) {
3617                 g_free(ainfo->name);
3618                 ainfo->name = NULL;
3619         }
3620
3621         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3622                 g_free(ainfo->content_type);
3623                 ainfo->content_type = g_strdup("application/octet-stream");
3624         }
3625
3626         ainfo->size = (goffset)size;
3627         size_text = to_human_readable((goffset)size);
3628
3629         store = GTK_LIST_STORE(gtk_tree_view_get_model
3630                         (GTK_TREE_VIEW(compose->attach_clist)));
3631                 
3632         gtk_list_store_append(store, &iter);
3633         gtk_list_store_set(store, &iter, 
3634                            COL_MIMETYPE, ainfo->content_type,
3635                            COL_SIZE, size_text,
3636                            COL_NAME, ainfo->name,
3637                            COL_DATA, ainfo,
3638                            COL_AUTODATA, auto_ainfo,
3639                            -1);
3640         
3641         g_auto_pointer_free(auto_ainfo);
3642         compose_attach_update_label(compose);
3643         return TRUE;
3644 }
3645
3646 static void compose_use_signing(Compose *compose, gboolean use_signing)
3647 {
3648         compose->use_signing = use_signing;
3649         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3650 }
3651
3652 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3653 {
3654         compose->use_encryption = use_encryption;
3655         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3656 }
3657
3658 #define NEXT_PART_NOT_CHILD(info)  \
3659 {  \
3660         node = info->node;  \
3661         while (node->children)  \
3662                 node = g_node_last_child(node);  \
3663         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3664 }
3665
3666 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3667 {
3668         MimeInfo *mimeinfo;
3669         MimeInfo *child;
3670         MimeInfo *firsttext = NULL;
3671         MimeInfo *encrypted = NULL;
3672         GNode    *node;
3673         gchar *outfile;
3674         const gchar *partname = NULL;
3675
3676         mimeinfo = procmime_scan_message(msginfo);
3677         if (!mimeinfo) return;
3678
3679         if (mimeinfo->node->children == NULL) {
3680                 procmime_mimeinfo_free_all(mimeinfo);
3681                 return;
3682         }
3683
3684         /* find first content part */
3685         child = (MimeInfo *) mimeinfo->node->children->data;
3686         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3687                 child = (MimeInfo *)child->node->children->data;
3688
3689         if (child) {
3690                 if (child->type == MIMETYPE_TEXT) {
3691                         firsttext = child;
3692                         debug_print("First text part found\n");
3693                 } else if (compose->mode == COMPOSE_REEDIT &&
3694                          child->type == MIMETYPE_APPLICATION &&
3695                          !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3696                         encrypted = (MimeInfo *)child->node->parent->data;
3697                 }
3698         }
3699         child = (MimeInfo *) mimeinfo->node->children->data;
3700         while (child != NULL) {
3701                 gint err;
3702
3703                 if (child == encrypted) {
3704                         /* skip this part of tree */
3705                         NEXT_PART_NOT_CHILD(child);
3706                         continue;
3707                 }
3708
3709                 if (child->type == MIMETYPE_MULTIPART) {
3710                         /* get the actual content */
3711                         child = procmime_mimeinfo_next(child);
3712                         continue;
3713                 }
3714                     
3715                 if (child == firsttext) {
3716                         child = procmime_mimeinfo_next(child);
3717                         continue;
3718                 }
3719
3720                 outfile = procmime_get_tmp_file_name(child);
3721                 if ((err = procmime_get_part(outfile, child)) < 0)
3722                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3723                 else {
3724                         gchar *content_type;
3725
3726                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3727
3728                         /* if we meet a pgp signature, we don't attach it, but
3729                          * we force signing. */
3730                         if ((strcmp(content_type, "application/pgp-signature") &&
3731                             strcmp(content_type, "application/pkcs7-signature") &&
3732                             strcmp(content_type, "application/x-pkcs7-signature"))
3733                             || compose->mode == COMPOSE_REDIRECT) {
3734                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3735                                 if (partname == NULL)
3736                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3737                                 if (partname == NULL)
3738                                         partname = "";
3739                                 compose_attach_append(compose, outfile, 
3740                                                       partname, content_type);
3741                         } else {
3742                                 compose_force_signing(compose, compose->account, NULL);
3743                         }
3744                         g_free(content_type);
3745                 }
3746                 g_free(outfile);
3747                 NEXT_PART_NOT_CHILD(child);
3748         }
3749         procmime_mimeinfo_free_all(mimeinfo);
3750 }
3751
3752 #undef NEXT_PART_NOT_CHILD
3753
3754
3755
3756 typedef enum {
3757         WAIT_FOR_INDENT_CHAR,
3758         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3759 } IndentState;
3760
3761 /* return indent length, we allow:
3762    indent characters followed by indent characters or spaces/tabs,
3763    alphabets and numbers immediately followed by indent characters,
3764    and the repeating sequences of the above
3765    If quote ends with multiple spaces, only the first one is included. */
3766 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3767                                     const GtkTextIter *start, gint *len)
3768 {
3769         GtkTextIter iter = *start;
3770         gunichar wc;
3771         gchar ch[6];
3772         gint clen;
3773         IndentState state = WAIT_FOR_INDENT_CHAR;
3774         gboolean is_space;
3775         gboolean is_indent;
3776         gint alnum_count = 0;
3777         gint space_count = 0;
3778         gint quote_len = 0;
3779
3780         if (prefs_common.quote_chars == NULL) {
3781                 return 0 ;
3782         }
3783
3784         while (!gtk_text_iter_ends_line(&iter)) {
3785                 wc = gtk_text_iter_get_char(&iter);
3786                 if (g_unichar_iswide(wc))
3787                         break;
3788                 clen = g_unichar_to_utf8(wc, ch);
3789                 if (clen != 1)
3790                         break;
3791
3792                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3793                 is_space = g_unichar_isspace(wc);
3794
3795                 if (state == WAIT_FOR_INDENT_CHAR) {
3796                         if (!is_indent && !g_unichar_isalnum(wc))
3797                                 break;
3798                         if (is_indent) {
3799                                 quote_len += alnum_count + space_count + 1;
3800                                 alnum_count = space_count = 0;
3801                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3802                         } else
3803                                 alnum_count++;
3804                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3805                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3806                                 break;
3807                         if (is_space)
3808                                 space_count++;
3809                         else if (is_indent) {
3810                                 quote_len += alnum_count + space_count + 1;
3811                                 alnum_count = space_count = 0;
3812                         } else {
3813                                 alnum_count++;
3814                                 state = WAIT_FOR_INDENT_CHAR;
3815                         }
3816                 }
3817
3818                 gtk_text_iter_forward_char(&iter);
3819         }
3820
3821         if (quote_len > 0 && space_count > 0)
3822                 quote_len++;
3823
3824         if (len)
3825                 *len = quote_len;
3826
3827         if (quote_len > 0) {
3828                 iter = *start;
3829                 gtk_text_iter_forward_chars(&iter, quote_len);
3830                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3831         }
3832
3833         return NULL;
3834 }
3835
3836 /* return >0 if the line is itemized */
3837 static int compose_itemized_length(GtkTextBuffer *buffer,
3838                                     const GtkTextIter *start)
3839 {
3840         GtkTextIter iter = *start;
3841         gunichar wc;
3842         gchar ch[6];
3843         gint clen;
3844         gint len = 0;
3845         if (gtk_text_iter_ends_line(&iter))
3846                 return 0;
3847
3848         while (1) {
3849                 len++;
3850                 wc = gtk_text_iter_get_char(&iter);
3851                 if (!g_unichar_isspace(wc))
3852                         break;
3853                 gtk_text_iter_forward_char(&iter);
3854                 if (gtk_text_iter_ends_line(&iter))
3855                         return 0;
3856         }
3857
3858         clen = g_unichar_to_utf8(wc, ch);
3859         if (clen != 1)
3860                 return 0;
3861
3862         if (!strchr("*-+", ch[0]))
3863                 return 0;
3864
3865         gtk_text_iter_forward_char(&iter);
3866         if (gtk_text_iter_ends_line(&iter))
3867                 return 0;
3868         wc = gtk_text_iter_get_char(&iter);
3869         if (g_unichar_isspace(wc)) {
3870                 return len+1;
3871         }
3872         return 0;
3873 }
3874
3875 /* return the string at the start of the itemization */
3876 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3877                                     const GtkTextIter *start)
3878 {
3879         GtkTextIter iter = *start;
3880         gunichar wc;
3881         gint len = 0;
3882         GString *item_chars = g_string_new("");
3883         gchar *str = NULL;
3884
3885         if (gtk_text_iter_ends_line(&iter))
3886                 return NULL;
3887
3888         while (1) {
3889                 len++;
3890                 wc = gtk_text_iter_get_char(&iter);
3891                 if (!g_unichar_isspace(wc))
3892                         break;
3893                 gtk_text_iter_forward_char(&iter);
3894                 if (gtk_text_iter_ends_line(&iter))
3895                         break;
3896                 g_string_append_unichar(item_chars, wc);
3897         }
3898
3899         str = item_chars->str;
3900         g_string_free(item_chars, FALSE);
3901         return str;
3902 }
3903
3904 /* return the number of spaces at a line's start */
3905 static int compose_left_offset_length(GtkTextBuffer *buffer,
3906                                     const GtkTextIter *start)
3907 {
3908         GtkTextIter iter = *start;
3909         gunichar wc;
3910         gint len = 0;
3911         if (gtk_text_iter_ends_line(&iter))
3912                 return 0;
3913
3914         while (1) {
3915                 wc = gtk_text_iter_get_char(&iter);
3916                 if (!g_unichar_isspace(wc))
3917                         break;
3918                 len++;
3919                 gtk_text_iter_forward_char(&iter);
3920                 if (gtk_text_iter_ends_line(&iter))
3921                         return 0;
3922         }
3923
3924         gtk_text_iter_forward_char(&iter);
3925         if (gtk_text_iter_ends_line(&iter))
3926                 return 0;
3927         return len;
3928 }
3929
3930 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3931                                            const GtkTextIter *start,
3932                                            GtkTextIter *break_pos,
3933                                            gint max_col,
3934                                            gint quote_len)
3935 {
3936         GtkTextIter iter = *start, line_end = *start;
3937         PangoLogAttr *attrs;
3938         gchar *str;
3939         gchar *p;
3940         gint len;
3941         gint i;
3942         gint col = 0;
3943         gint pos = 0;
3944         gboolean can_break = FALSE;
3945         gboolean do_break = FALSE;
3946         gboolean was_white = FALSE;
3947         gboolean prev_dont_break = FALSE;
3948
3949         gtk_text_iter_forward_to_line_end(&line_end);
3950         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3951         len = g_utf8_strlen(str, -1);
3952         
3953         if (len == 0) {
3954                 g_free(str);
3955                 g_warning("compose_get_line_break_pos: len = 0!\n");
3956                 return FALSE;
3957         }
3958
3959         /* g_print("breaking line: %d: %s (len = %d)\n",
3960                 gtk_text_iter_get_line(&iter), str, len); */
3961
3962         attrs = g_new(PangoLogAttr, len + 1);
3963
3964         pango_default_break(str, -1, NULL, attrs, len + 1);
3965
3966         p = str;
3967
3968         /* skip quote and leading spaces */
3969         for (i = 0; *p != '\0' && i < len; i++) {
3970                 gunichar wc;
3971
3972                 wc = g_utf8_get_char(p);
3973                 if (i >= quote_len && !g_unichar_isspace(wc))
3974                         break;
3975                 if (g_unichar_iswide(wc))
3976                         col += 2;
3977                 else if (*p == '\t')
3978                         col += 8;
3979                 else
3980                         col++;
3981                 p = g_utf8_next_char(p);
3982         }
3983
3984         for (; *p != '\0' && i < len; i++) {
3985                 PangoLogAttr *attr = attrs + i;
3986                 gunichar wc;
3987                 gint uri_len;
3988
3989                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3990                         pos = i;
3991                 
3992                 was_white = attr->is_white;
3993
3994                 /* don't wrap URI */
3995                 if ((uri_len = get_uri_len(p)) > 0) {
3996                         col += uri_len;
3997                         if (pos > 0 && col > max_col) {
3998                                 do_break = TRUE;
3999                                 break;
4000                         }
4001                         i += uri_len - 1;
4002                         p += uri_len;
4003                         can_break = TRUE;
4004                         continue;
4005                 }
4006
4007                 wc = g_utf8_get_char(p);
4008                 if (g_unichar_iswide(wc)) {
4009                         col += 2;
4010                         if (prev_dont_break && can_break && attr->is_line_break)
4011                                 pos = i;
4012                 } else if (*p == '\t')
4013                         col += 8;
4014                 else
4015                         col++;
4016                 if (pos > 0 && col > max_col) {
4017                         do_break = TRUE;
4018                         break;
4019                 }
4020
4021                 if (*p == '-' || *p == '/')
4022                         prev_dont_break = TRUE;
4023                 else
4024                         prev_dont_break = FALSE;
4025
4026                 p = g_utf8_next_char(p);
4027                 can_break = TRUE;
4028         }
4029
4030 //      debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4031
4032         g_free(attrs);
4033         g_free(str);
4034
4035         *break_pos = *start;
4036         gtk_text_iter_set_line_offset(break_pos, pos);
4037
4038         return do_break;
4039 }
4040
4041 static gboolean compose_join_next_line(Compose *compose,
4042                                        GtkTextBuffer *buffer,
4043                                        GtkTextIter *iter,
4044                                        const gchar *quote_str)
4045 {
4046         GtkTextIter iter_ = *iter, cur, prev, next, end;
4047         PangoLogAttr attrs[3];
4048         gchar *str;
4049         gchar *next_quote_str;
4050         gunichar wc1, wc2;
4051         gint quote_len;
4052         gboolean keep_cursor = FALSE;
4053
4054         if (!gtk_text_iter_forward_line(&iter_) ||
4055             gtk_text_iter_ends_line(&iter_)) {
4056                 return FALSE;
4057         }
4058         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
4059
4060         if ((quote_str || next_quote_str) &&
4061             strcmp2(quote_str, next_quote_str) != 0) {
4062                 g_free(next_quote_str);
4063                 return FALSE;
4064         }
4065         g_free(next_quote_str);
4066
4067         end = iter_;
4068         if (quote_len > 0) {
4069                 gtk_text_iter_forward_chars(&end, quote_len);
4070                 if (gtk_text_iter_ends_line(&end)) {
4071                         return FALSE;
4072                 }
4073         }
4074
4075         /* don't join itemized lines */
4076         if (compose_itemized_length(buffer, &end) > 0) {
4077                 return FALSE;
4078         }
4079
4080         /* don't join signature separator */
4081         if (compose_is_sig_separator(compose, buffer, &iter_)) {
4082                 return FALSE;
4083         }
4084         /* delete quote str */
4085         if (quote_len > 0)
4086                 gtk_text_buffer_delete(buffer, &iter_, &end);
4087
4088         /* don't join line breaks put by the user */
4089         prev = cur = iter_;
4090         gtk_text_iter_backward_char(&cur);
4091         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4092                 gtk_text_iter_forward_char(&cur);
4093                 *iter = cur;
4094                 return FALSE;
4095         }
4096         gtk_text_iter_forward_char(&cur);
4097         /* delete linebreak and extra spaces */
4098         while (gtk_text_iter_backward_char(&cur)) {
4099                 wc1 = gtk_text_iter_get_char(&cur);
4100                 if (!g_unichar_isspace(wc1))
4101                         break;
4102                 prev = cur;
4103         }
4104         next = cur = iter_;
4105         while (!gtk_text_iter_ends_line(&cur)) {
4106                 wc1 = gtk_text_iter_get_char(&cur);
4107                 if (!g_unichar_isspace(wc1))
4108                         break;
4109                 gtk_text_iter_forward_char(&cur);
4110                 next = cur;
4111         }
4112         if (!gtk_text_iter_equal(&prev, &next)) {
4113                 GtkTextMark *mark;
4114
4115                 mark = gtk_text_buffer_get_insert(buffer);
4116                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4117                 if (gtk_text_iter_equal(&prev, &cur))
4118                         keep_cursor = TRUE;
4119                 gtk_text_buffer_delete(buffer, &prev, &next);
4120         }
4121         iter_ = prev;
4122
4123         /* insert space if required */
4124         gtk_text_iter_backward_char(&prev);
4125         wc1 = gtk_text_iter_get_char(&prev);
4126         wc2 = gtk_text_iter_get_char(&next);
4127         gtk_text_iter_forward_char(&next);
4128         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4129         pango_default_break(str, -1, NULL, attrs, 3);
4130         if (!attrs[1].is_line_break ||
4131             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4132                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4133                 if (keep_cursor) {
4134                         gtk_text_iter_backward_char(&iter_);
4135                         gtk_text_buffer_place_cursor(buffer, &iter_);
4136                 }
4137         }
4138         g_free(str);
4139
4140         *iter = iter_;
4141         return TRUE;
4142 }
4143
4144 #define ADD_TXT_POS(bp_, ep_, pti_) \
4145         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4146                 last = last->next; \
4147                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4148                 last->next = NULL; \
4149         } else { \
4150                 g_warning("alloc error scanning URIs\n"); \
4151         }
4152
4153 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4154 {
4155         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4156         GtkTextBuffer *buffer;
4157         GtkTextIter iter, break_pos, end_of_line;
4158         gchar *quote_str = NULL;
4159         gint quote_len;
4160         gboolean wrap_quote = prefs_common.linewrap_quote;
4161         gboolean prev_autowrap = compose->autowrap;
4162         gint startq_offset = -1, noq_offset = -1;
4163         gint uri_start = -1, uri_stop = -1;
4164         gint nouri_start = -1, nouri_stop = -1;
4165         gint num_blocks = 0;
4166         gint quotelevel = -1;
4167         gboolean modified = force;
4168         gboolean removed = FALSE;
4169         gboolean modified_before_remove = FALSE;
4170         gint lines = 0;
4171         gboolean start = TRUE;
4172         gint itemized_len = 0, rem_item_len = 0;
4173         gchar *itemized_chars = NULL;
4174         gboolean item_continuation = FALSE;
4175
4176         if (force) {
4177                 modified = TRUE;
4178         }
4179         if (compose->draft_timeout_tag == -2) {
4180                 modified = TRUE;
4181         }
4182
4183         compose->autowrap = FALSE;
4184
4185         buffer = gtk_text_view_get_buffer(text);
4186         undo_wrapping(compose->undostruct, TRUE);
4187         if (par_iter) {
4188                 iter = *par_iter;
4189         } else {
4190                 GtkTextMark *mark;
4191                 mark = gtk_text_buffer_get_insert(buffer);
4192                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4193         }
4194
4195
4196         if (compose->draft_timeout_tag == -2) {
4197                 if (gtk_text_iter_ends_line(&iter)) {
4198                         while (gtk_text_iter_ends_line(&iter) &&
4199                                gtk_text_iter_forward_line(&iter))
4200                                 ;
4201                 } else {
4202                         while (gtk_text_iter_backward_line(&iter)) {
4203                                 if (gtk_text_iter_ends_line(&iter)) {
4204                                         gtk_text_iter_forward_line(&iter);
4205                                         break;
4206                                 }
4207                         }
4208                 }
4209         } else {
4210                 /* move to line start */
4211                 gtk_text_iter_set_line_offset(&iter, 0);
4212         }
4213         
4214         itemized_len = compose_itemized_length(buffer, &iter);
4215         
4216         if (!itemized_len) {
4217                 itemized_len = compose_left_offset_length(buffer, &iter);
4218                 item_continuation = TRUE;
4219         }
4220
4221         if (itemized_len)
4222                 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4223
4224         /* go until paragraph end (empty line) */
4225         while (start || !gtk_text_iter_ends_line(&iter)) {
4226                 gchar *scanpos = NULL;
4227                 /* parse table - in order of priority */
4228                 struct table {
4229                         const gchar *needle; /* token */
4230
4231                         /* token search function */
4232                         gchar    *(*search)     (const gchar *haystack,
4233                                                  const gchar *needle);
4234                         /* part parsing function */
4235                         gboolean  (*parse)      (const gchar *start,
4236                                                  const gchar *scanpos,
4237                                                  const gchar **bp_,
4238                                                  const gchar **ep_,
4239                                                  gboolean hdr);
4240                         /* part to URI function */
4241                         gchar    *(*build_uri)  (const gchar *bp,
4242                                                  const gchar *ep);
4243                 };
4244
4245                 static struct table parser[] = {
4246                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
4247                         {"https://", strcasestr, get_uri_part,   make_uri_string},
4248                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
4249                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
4250                         {"gopher://",strcasestr, get_uri_part,   make_uri_string},
4251                         {"www.",     strcasestr, get_uri_part,   make_http_string},
4252                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
4253                         {"@",        strcasestr, get_email_part, make_email_string}
4254                 };
4255                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4256                 gint last_index = PARSE_ELEMS;
4257                 gint  n;
4258                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4259                 gint walk_pos;
4260                 
4261                 start = FALSE;
4262                 if (!prev_autowrap && num_blocks == 0) {
4263                         num_blocks++;
4264                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
4265                                         G_CALLBACK(text_inserted),
4266                                         compose);
4267                 }
4268                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4269                         goto colorize;
4270
4271                 uri_start = uri_stop = -1;
4272                 quote_len = 0;
4273                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
4274
4275                 if (quote_str) {
4276 //                      debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4277                         if (startq_offset == -1) 
4278                                 startq_offset = gtk_text_iter_get_offset(&iter);
4279                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4280                         if (quotelevel > 2) {
4281                                 /* recycle colors */
4282                                 if (prefs_common.recycle_quote_colors)
4283                                         quotelevel %= 3;
4284                                 else
4285                                         quotelevel = 2;
4286                         }
4287                         if (!wrap_quote) {
4288                                 goto colorize;
4289                         }
4290                 } else {
4291                         if (startq_offset == -1)
4292                                 noq_offset = gtk_text_iter_get_offset(&iter);
4293                         quotelevel = -1;
4294                 }
4295
4296                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4297                         goto colorize;
4298                 }
4299                 if (gtk_text_iter_ends_line(&iter)) {
4300                         goto colorize;
4301                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4302                                                prefs_common.linewrap_len,
4303                                                quote_len)) {
4304                         GtkTextIter prev, next, cur;
4305                         if (prev_autowrap != FALSE || force) {
4306                                 compose->automatic_break = TRUE;
4307                                 modified = TRUE;
4308                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4309                                 compose->automatic_break = FALSE;
4310                                 if (itemized_len && compose->autoindent) {
4311                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4312                                         if (!item_continuation)
4313                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4314                                 }
4315                         } else if (quote_str && wrap_quote) {
4316                                 compose->automatic_break = TRUE;
4317                                 modified = TRUE;
4318                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4319                                 compose->automatic_break = FALSE;
4320                                 if (itemized_len && compose->autoindent) {
4321                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4322                                         if (!item_continuation)
4323                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4324                                 }
4325                         } else 
4326                                 goto colorize;
4327                         /* remove trailing spaces */
4328                         cur = break_pos;
4329                         rem_item_len = itemized_len;
4330                         while (compose->autoindent && rem_item_len-- > 0)
4331                                 gtk_text_iter_backward_char(&cur);
4332                         gtk_text_iter_backward_char(&cur);
4333
4334                         prev = next = cur;
4335                         while (!gtk_text_iter_starts_line(&cur)) {
4336                                 gunichar wc;
4337
4338                                 gtk_text_iter_backward_char(&cur);
4339                                 wc = gtk_text_iter_get_char(&cur);
4340                                 if (!g_unichar_isspace(wc))
4341                                         break;
4342                                 prev = cur;
4343                         }
4344                         if (!gtk_text_iter_equal(&prev, &next)) {
4345                                 gtk_text_buffer_delete(buffer, &prev, &next);
4346                                 break_pos = next;
4347                                 gtk_text_iter_forward_char(&break_pos);
4348                         }
4349
4350                         if (quote_str)
4351                                 gtk_text_buffer_insert(buffer, &break_pos,
4352                                                        quote_str, -1);
4353
4354                         iter = break_pos;
4355                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4356
4357                         /* move iter to current line start */
4358                         gtk_text_iter_set_line_offset(&iter, 0);
4359                         if (quote_str) {
4360                                 g_free(quote_str);
4361                                 quote_str = NULL;
4362                         }
4363                         continue;       
4364                 } else {
4365                         /* move iter to next line start */
4366                         iter = break_pos;
4367                         lines++;
4368                 }
4369
4370 colorize:
4371                 if (!prev_autowrap && num_blocks > 0) {
4372                         num_blocks--;
4373                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4374                                         G_CALLBACK(text_inserted),
4375                                         compose);
4376                 }
4377                 end_of_line = iter;
4378                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4379                         gtk_text_iter_forward_char(&end_of_line);
4380                 }
4381                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4382
4383                 nouri_start = gtk_text_iter_get_offset(&iter);
4384                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4385
4386                 walk_pos = gtk_text_iter_get_offset(&iter);
4387                 /* FIXME: this looks phony. scanning for anything in the parse table */
4388                 for (n = 0; n < PARSE_ELEMS; n++) {
4389                         gchar *tmp;
4390
4391                         tmp = parser[n].search(walk, parser[n].needle);
4392                         if (tmp) {
4393                                 if (scanpos == NULL || tmp < scanpos) {
4394                                         scanpos = tmp;
4395                                         last_index = n;
4396                                 }
4397                         }                                       
4398                 }
4399
4400                 bp = ep = 0;
4401                 if (scanpos) {
4402                         /* check if URI can be parsed */
4403                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4404                                         (const gchar **)&ep, FALSE)
4405                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4406                                         walk = ep;
4407                         } else
4408                                 walk = scanpos +
4409                                         strlen(parser[last_index].needle);
4410                 } 
4411                 if (bp && ep) {
4412                         uri_start = walk_pos + (bp - o_walk);
4413                         uri_stop  = walk_pos + (ep - o_walk);
4414                 }
4415                 g_free(o_walk);
4416                 o_walk = NULL;
4417                 gtk_text_iter_forward_line(&iter);
4418                 g_free(quote_str);
4419                 quote_str = NULL;
4420                 if (startq_offset != -1) {
4421                         GtkTextIter startquote, endquote;
4422                         gtk_text_buffer_get_iter_at_offset(
4423                                 buffer, &startquote, startq_offset);
4424                         endquote = iter;
4425
4426                         switch (quotelevel) {
4427                         case 0: 
4428                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4429                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4430                                         gtk_text_buffer_apply_tag_by_name(
4431                                                 buffer, "quote0", &startquote, &endquote);
4432                                         gtk_text_buffer_remove_tag_by_name(
4433                                                 buffer, "quote1", &startquote, &endquote);
4434                                         gtk_text_buffer_remove_tag_by_name(
4435                                                 buffer, "quote2", &startquote, &endquote);
4436                                         modified = TRUE;
4437                                 }
4438                                 break;
4439                         case 1: 
4440                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4441                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4442                                         gtk_text_buffer_apply_tag_by_name(
4443                                                 buffer, "quote1", &startquote, &endquote);
4444                                         gtk_text_buffer_remove_tag_by_name(
4445                                                 buffer, "quote0", &startquote, &endquote);
4446                                         gtk_text_buffer_remove_tag_by_name(
4447                                                 buffer, "quote2", &startquote, &endquote);
4448                                         modified = TRUE;
4449                                 }
4450                                 break;
4451                         case 2: 
4452                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4453                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4454                                         gtk_text_buffer_apply_tag_by_name(
4455                                                 buffer, "quote2", &startquote, &endquote);
4456                                         gtk_text_buffer_remove_tag_by_name(
4457                                                 buffer, "quote0", &startquote, &endquote);
4458                                         gtk_text_buffer_remove_tag_by_name(
4459                                                 buffer, "quote1", &startquote, &endquote);
4460                                         modified = TRUE;
4461                                 }
4462                                 break;
4463                         }
4464                         startq_offset = -1;
4465                 } else if (noq_offset != -1) {
4466                         GtkTextIter startnoquote, endnoquote;
4467                         gtk_text_buffer_get_iter_at_offset(
4468                                 buffer, &startnoquote, noq_offset);
4469                         endnoquote = iter;
4470
4471                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4472                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4473                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4474                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4475                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4476                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4477                                 gtk_text_buffer_remove_tag_by_name(
4478                                         buffer, "quote0", &startnoquote, &endnoquote);
4479                                 gtk_text_buffer_remove_tag_by_name(
4480                                         buffer, "quote1", &startnoquote, &endnoquote);
4481                                 gtk_text_buffer_remove_tag_by_name(
4482                                         buffer, "quote2", &startnoquote, &endnoquote);
4483                                 modified = TRUE;
4484                         }
4485                         noq_offset = -1;
4486                 }
4487                 
4488                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4489                         GtkTextIter nouri_start_iter, nouri_end_iter;
4490                         gtk_text_buffer_get_iter_at_offset(
4491                                 buffer, &nouri_start_iter, nouri_start);
4492                         gtk_text_buffer_get_iter_at_offset(
4493                                 buffer, &nouri_end_iter, nouri_stop);
4494                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4495                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4496                                 gtk_text_buffer_remove_tag_by_name(
4497                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4498                                 modified_before_remove = modified;
4499                                 modified = TRUE;
4500                                 removed = TRUE;
4501                         }
4502                 }
4503                 if (uri_start >= 0 && uri_stop > 0) {
4504                         GtkTextIter uri_start_iter, uri_end_iter, back;
4505                         gtk_text_buffer_get_iter_at_offset(
4506                                 buffer, &uri_start_iter, uri_start);
4507                         gtk_text_buffer_get_iter_at_offset(
4508                                 buffer, &uri_end_iter, uri_stop);
4509                         back = uri_end_iter;
4510                         gtk_text_iter_backward_char(&back);
4511                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4512                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4513                                 gtk_text_buffer_apply_tag_by_name(
4514                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4515                                 modified = TRUE;
4516                                 if (removed && !modified_before_remove) {
4517                                         modified = FALSE;
4518                                 } 
4519                         }
4520                 }
4521                 if (!modified) {
4522 //                      debug_print("not modified, out after %d lines\n", lines);
4523                         goto end;
4524                 }
4525         }
4526 //      debug_print("modified, out after %d lines\n", lines);
4527 end:
4528         g_free(itemized_chars);
4529         if (par_iter)
4530                 *par_iter = iter;
4531         undo_wrapping(compose->undostruct, FALSE);
4532         compose->autowrap = prev_autowrap;
4533         
4534         return modified;
4535 }
4536
4537 void compose_action_cb(void *data)
4538 {
4539         Compose *compose = (Compose *)data;
4540         compose_wrap_all(compose);
4541 }
4542
4543 static void compose_wrap_all(Compose *compose)
4544 {
4545         compose_wrap_all_full(compose, FALSE);
4546 }
4547
4548 static void compose_wrap_all_full(Compose *compose, gboolean force)
4549 {
4550         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4551         GtkTextBuffer *buffer;
4552         GtkTextIter iter;
4553         gboolean modified = TRUE;
4554
4555         buffer = gtk_text_view_get_buffer(text);
4556
4557         gtk_text_buffer_get_start_iter(buffer, &iter);
4558         while (!gtk_text_iter_is_end(&iter) && modified)
4559                 modified = compose_beautify_paragraph(compose, &iter, force);
4560
4561 }
4562
4563 static void compose_set_title(Compose *compose)
4564 {
4565         gchar *str;
4566         gchar *edited;
4567         gchar *subject;
4568         
4569         edited = compose->modified ? _(" [Edited]") : "";
4570         
4571         subject = gtk_editable_get_chars(
4572                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4573
4574 #ifndef GENERIC_UMPC
4575         if (subject && strlen(subject))
4576                 str = g_strdup_printf(_("%s - Compose message%s"),
4577                                       subject, edited); 
4578         else
4579                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4580 #else
4581         str = g_strdup(_("Compose message"));
4582 #endif
4583
4584         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4585         g_free(str);
4586         g_free(subject);
4587 }
4588
4589 /**
4590  * compose_current_mail_account:
4591  * 
4592  * Find a current mail account (the currently selected account, or the
4593  * default account, if a news account is currently selected).  If a
4594  * mail account cannot be found, display an error message.
4595  * 
4596  * Return value: Mail account, or NULL if not found.
4597  **/
4598 static PrefsAccount *
4599 compose_current_mail_account(void)
4600 {
4601         PrefsAccount *ac;
4602
4603         if (cur_account && cur_account->protocol != A_NNTP)
4604                 ac = cur_account;
4605         else {
4606                 ac = account_get_default();
4607                 if (!ac || ac->protocol == A_NNTP) {
4608                         alertpanel_error(_("Account for sending mail is not specified.\n"
4609                                            "Please select a mail account before sending."));
4610                         return NULL;
4611                 }
4612         }
4613         return ac;
4614 }
4615
4616 #define QUOTE_IF_REQUIRED(out, str)                                     \
4617 {                                                                       \
4618         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4619                 gchar *__tmp;                                           \
4620                 gint len;                                               \
4621                                                                         \
4622                 len = strlen(str) + 3;                                  \
4623                 if ((__tmp = alloca(len)) == NULL) {                    \
4624                         g_warning("can't allocate memory\n");           \
4625                         g_string_free(header, TRUE);                    \
4626                         return NULL;                                    \
4627                 }                                                       \
4628                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4629                 out = __tmp;                                            \
4630         } else {                                                        \
4631                 gchar *__tmp;                                           \
4632                                                                         \
4633                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4634                         g_warning("can't allocate memory\n");           \
4635                         g_string_free(header, TRUE);                    \
4636                         return NULL;                                    \
4637                 } else                                                  \
4638                         strcpy(__tmp, str);                             \
4639                                                                         \
4640                 out = __tmp;                                            \
4641         }                                                               \
4642 }
4643
4644 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4645 {                                                                       \
4646         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4647                 gchar *__tmp;                                           \
4648                 gint len;                                               \
4649                                                                         \
4650                 len = strlen(str) + 3;                                  \
4651                 if ((__tmp = alloca(len)) == NULL) {                    \
4652                         g_warning("can't allocate memory\n");           \
4653                         errret;                                         \
4654                 }                                                       \
4655                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4656                 out = __tmp;                                            \
4657         } else {                                                        \
4658                 gchar *__tmp;                                           \
4659                                                                         \
4660                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4661                         g_warning("can't allocate memory\n");           \
4662                         errret;                                         \
4663                 } else                                                  \
4664                         strcpy(__tmp, str);                             \
4665                                                                         \
4666                 out = __tmp;                                            \
4667         }                                                               \
4668 }
4669
4670 static void compose_select_account(Compose *compose, PrefsAccount *account,
4671                                    gboolean init)
4672 {
4673         gchar *from = NULL, *header;
4674         ComposeHeaderEntry *header_entry;
4675
4676         cm_return_if_fail(account != NULL);
4677
4678         compose->account = account;
4679         if (account->name && *account->name) {
4680                 gchar *buf;
4681                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4682                 from = g_strdup_printf("%s <%s>",
4683                                        buf, account->address);
4684                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4685         } else {
4686                 from = g_strdup_printf("<%s>",
4687                                        account->address);
4688                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4689         }
4690
4691         g_free(from);
4692
4693         compose_set_title(compose);
4694
4695         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4696                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4697         else
4698                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4699         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4700                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4701         else
4702                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4703                                        
4704         activate_privacy_system(compose, account, FALSE);
4705
4706         if (!init && compose->mode != COMPOSE_REDIRECT) {
4707                 undo_block(compose->undostruct);
4708                 compose_insert_sig(compose, TRUE);
4709                 undo_unblock(compose->undostruct);
4710         }
4711         
4712         header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4713         header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4714         
4715         if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4716                 if (account->protocol == A_NNTP) {
4717                         if (!strcmp(header, _("To:")))
4718                                 combobox_select_by_text(
4719                                         GTK_COMBO_BOX(header_entry->combo),
4720                                         _("Newsgroups:"));
4721                 } else {
4722                         if (!strcmp(header, _("Newsgroups:")))
4723                                 combobox_select_by_text(
4724                                         GTK_COMBO_BOX(header_entry->combo),
4725                                         _("To:"));
4726                 }
4727                 
4728         }
4729         g_free(header);
4730         
4731 #ifdef USE_ENCHANT
4732         /* use account's dict info if set */
4733         if (compose->gtkaspell) {
4734                 if (account->enable_default_dictionary)
4735                         gtkaspell_change_dict(compose->gtkaspell,
4736                                         account->default_dictionary, FALSE);
4737                 if (account->enable_default_alt_dictionary)
4738                         gtkaspell_change_alt_dict(compose->gtkaspell,
4739                                         account->default_alt_dictionary);
4740                 if (account->enable_default_dictionary
4741                         || account->enable_default_alt_dictionary)
4742                         compose_spell_menu_changed(compose);
4743         }
4744 #endif
4745 }
4746
4747 gboolean compose_check_for_valid_recipient(Compose *compose) {
4748         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4749         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4750         gboolean recipient_found = FALSE;
4751         GSList *list;
4752         gchar **strptr;
4753
4754         /* free to and newsgroup list */
4755         slist_free_strings(compose->to_list);
4756         g_slist_free(compose->to_list);
4757         compose->to_list = NULL;
4758                         
4759         slist_free_strings(compose->newsgroup_list);
4760         g_slist_free(compose->newsgroup_list);
4761         compose->newsgroup_list = NULL;
4762
4763         /* search header entries for to and newsgroup entries */
4764         for (list = compose->header_list; list; list = list->next) {
4765                 gchar *header;
4766                 gchar *entry;
4767                 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4768                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4769                 g_strstrip(entry);
4770                 g_strstrip(header);
4771                 if (entry[0] != '\0') {
4772                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4773                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4774                                         compose->to_list = address_list_append(compose->to_list, entry);
4775                                         recipient_found = TRUE;
4776                                 }
4777                         }
4778                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4779                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4780                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4781                                         recipient_found = TRUE;
4782                                 }
4783                         }
4784                 }
4785                 g_free(header);
4786                 g_free(entry);
4787         }
4788         return recipient_found;
4789 }
4790
4791 static gboolean compose_check_for_set_recipients(Compose *compose)
4792 {
4793         if (compose->account->set_autocc && compose->account->auto_cc) {
4794                 gboolean found_other = FALSE;
4795                 GSList *list;
4796                 /* search header entries for to and newsgroup entries */
4797                 for (list = compose->header_list; list; list = list->next) {
4798                         gchar *entry;
4799                         gchar *header;
4800                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4801                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4802                         g_strstrip(entry);
4803                         g_strstrip(header);
4804                         if (strcmp(entry, compose->account->auto_cc)
4805                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4806                                 found_other = TRUE;
4807                                 g_free(entry);
4808                                 break;
4809                         }
4810                         g_free(entry);
4811                         g_free(header);
4812                 }
4813                 if (!found_other) {
4814                         AlertValue aval;
4815                         if (compose->batch) {
4816                                 gtk_widget_show_all(compose->window);
4817                         }
4818                         aval = alertpanel(_("Send"),
4819                                           _("The only recipient is the default CC address. Send anyway?"),
4820                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4821                         if (aval != G_ALERTALTERNATE)
4822                                 return FALSE;
4823                 }
4824         }
4825         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4826                 gboolean found_other = FALSE;
4827                 GSList *list;
4828                 /* search header entries for to and newsgroup entries */
4829                 for (list = compose->header_list; list; list = list->next) {
4830                         gchar *entry;
4831                         gchar *header;
4832                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4833                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4834                         g_strstrip(entry);
4835                         g_strstrip(header);
4836                         if (strcmp(entry, compose->account->auto_bcc)
4837                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4838                                 found_other = TRUE;
4839                                 g_free(entry);
4840                                 break;
4841                         }
4842                         g_free(entry);
4843                         g_free(header);
4844                 }
4845                 if (!found_other) {
4846                         AlertValue aval;
4847                         if (compose->batch) {
4848                                 gtk_widget_show_all(compose->window);
4849                         }
4850                         aval = alertpanel(_("Send"),
4851                                           _("The only recipient is the default BCC address. Send anyway?"),
4852                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4853                         if (aval != G_ALERTALTERNATE)
4854                                 return FALSE;
4855                 }
4856         }
4857         return TRUE;
4858 }
4859
4860 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4861 {
4862         const gchar *str;
4863
4864         if (compose_check_for_valid_recipient(compose) == FALSE) {
4865                 if (compose->batch) {
4866                         gtk_widget_show_all(compose->window);
4867                 }
4868                 alertpanel_error(_("Recipient is not specified."));
4869                 return FALSE;
4870         }
4871
4872         if (compose_check_for_set_recipients(compose) == FALSE) {
4873                 return FALSE;
4874         }
4875
4876         if (!compose->batch) {
4877                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4878                 if (*str == '\0' && check_everything == TRUE && 
4879                     compose->mode != COMPOSE_REDIRECT) {
4880                         AlertValue aval;
4881                         gchar *button_label;
4882                         gchar *message;
4883
4884                         if (compose->sending)
4885                                 button_label = _("+_Send");
4886                         else
4887                                 button_label = _("+_Queue");
4888                         message = g_strdup_printf(_("Subject is empty. %s"),
4889                                         compose->sending?_("Send it anyway?"):
4890                                         _("Queue it anyway?"));
4891
4892                         aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4893                                           GTK_STOCK_CANCEL, button_label, NULL);
4894                         g_free(message);
4895                         if (aval != G_ALERTALTERNATE)
4896                                 return FALSE;
4897                 }
4898         }
4899
4900         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4901                 return FALSE;
4902
4903         return TRUE;
4904 }
4905
4906 gint compose_send(Compose *compose)
4907 {
4908         gint msgnum;
4909         FolderItem *folder = NULL;
4910         gint val = -1;
4911         gchar *msgpath = NULL;
4912         gboolean discard_window = FALSE;
4913         gchar *errstr = NULL;
4914         gchar *tmsgid = NULL;
4915         MainWindow *mainwin = mainwindow_get_mainwindow();
4916         gboolean queued_removed = FALSE;
4917
4918         if (prefs_common.send_dialog_invisible
4919                         || compose->batch == TRUE)
4920                 discard_window = TRUE;
4921
4922         compose_allow_user_actions (compose, FALSE);
4923         compose->sending = TRUE;
4924
4925         if (compose_check_entries(compose, TRUE) == FALSE) {
4926                 if (compose->batch) {
4927                         gtk_widget_show_all(compose->window);
4928                 }
4929                 goto bail;
4930         }
4931
4932         inc_lock();
4933         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4934
4935         if (val) {
4936                 if (compose->batch) {
4937                         gtk_widget_show_all(compose->window);
4938                 }
4939                 if (val == -4) {
4940                         alertpanel_error(_("Could not queue message for sending:\n\n"
4941                                            "Charset conversion failed."));
4942                 } else if (val == -5) {
4943                         alertpanel_error(_("Could not queue message for sending:\n\n"
4944                                            "Couldn't get recipient encryption key."));
4945                 } else if (val == -6) {
4946                         /* silent error */
4947                 } else if (val == -3) {
4948                         if (privacy_peek_error())
4949                         alertpanel_error(_("Could not queue message for sending:\n\n"
4950                                            "Signature failed: %s"), privacy_get_error());
4951                 } else if (val == -2 && errno != 0) {
4952                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4953                 } else {
4954                         alertpanel_error(_("Could not queue message for sending."));
4955                 }
4956                 goto bail;
4957         }
4958
4959         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4960         if (discard_window) {
4961                 compose->sending = FALSE;
4962                 compose_close(compose);
4963                 /* No more compose access in the normal codepath 
4964                  * after this point! */
4965                 compose = NULL;
4966         }
4967
4968         if (msgnum == 0) {
4969                 alertpanel_error(_("The message was queued but could not be "
4970                                    "sent.\nUse \"Send queued messages\" from "
4971                                    "the main window to retry."));
4972                 if (!discard_window) {
4973                         goto bail;
4974                 }
4975                 inc_unlock();
4976                 g_free(tmsgid);
4977                 return -1;
4978         }
4979         if (msgpath == NULL) {
4980                 msgpath = folder_item_fetch_msg(folder, msgnum);
4981                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4982                 g_free(msgpath);
4983         } else {
4984                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4985                 claws_unlink(msgpath);
4986                 g_free(msgpath);
4987         }
4988         if (!discard_window) {
4989                 if (val != 0) {
4990                         if (!queued_removed)
4991                                 folder_item_remove_msg(folder, msgnum);
4992                         folder_item_scan(folder);
4993                         if (tmsgid) {
4994                                 /* make sure we delete that */
4995                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4996                                 if (tmp) {
4997                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4998                                         folder_item_remove_msg(folder, tmp->msgnum);
4999                                         procmsg_msginfo_free(tmp);
5000                                 } 
5001                         }
5002                 }
5003         }
5004
5005         if (val == 0) {
5006                 if (!queued_removed)
5007                         folder_item_remove_msg(folder, msgnum);
5008                 folder_item_scan(folder);
5009                 if (tmsgid) {
5010                         /* make sure we delete that */
5011                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5012                         if (tmp) {
5013                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5014                                 folder_item_remove_msg(folder, tmp->msgnum);
5015                                 procmsg_msginfo_free(tmp);
5016                         }
5017                 }
5018                 if (!discard_window) {
5019                         compose->sending = FALSE;
5020                         compose_allow_user_actions (compose, TRUE);
5021                         compose_close(compose);
5022                 }
5023         } else {
5024                 if (errstr) {
5025                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5026                                    "the main window to retry."), errstr);
5027                         g_free(errstr);
5028                 } else {
5029                         alertpanel_error_log(_("The message was queued but could not be "
5030                                    "sent.\nUse \"Send queued messages\" from "
5031                                    "the main window to retry."));
5032                 }
5033                 if (!discard_window) {
5034                         goto bail;              
5035                 }
5036                 inc_unlock();
5037                 g_free(tmsgid);
5038                 return -1;
5039         }
5040         g_free(tmsgid);
5041         inc_unlock();
5042         toolbar_main_set_sensitive(mainwin);
5043         main_window_set_menu_sensitive(mainwin);
5044         return 0;
5045
5046 bail:
5047         inc_unlock();
5048         g_free(tmsgid);
5049         compose_allow_user_actions (compose, TRUE);
5050         compose->sending = FALSE;
5051         compose->modified = TRUE; 
5052         toolbar_main_set_sensitive(mainwin);
5053         main_window_set_menu_sensitive(mainwin);
5054
5055         return -1;
5056 }
5057
5058 static gboolean compose_use_attach(Compose *compose) 
5059 {
5060         GtkTreeModel *model = gtk_tree_view_get_model
5061                                 (GTK_TREE_VIEW(compose->attach_clist));
5062         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5063 }
5064
5065 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5066                                                            FILE *fp)
5067 {
5068         gchar buf[BUFFSIZE];
5069         gchar *str;
5070         gboolean first_to_address;
5071         gboolean first_cc_address;
5072         GSList *list;
5073         ComposeHeaderEntry *headerentry;
5074         const gchar *headerentryname;
5075         const gchar *cc_hdr;
5076         const gchar *to_hdr;
5077         gboolean err = FALSE;
5078
5079         debug_print("Writing redirect header\n");
5080
5081         cc_hdr = prefs_common_translated_header_name("Cc:");
5082         to_hdr = prefs_common_translated_header_name("To:");
5083
5084         first_to_address = TRUE;
5085         for (list = compose->header_list; list; list = list->next) {
5086                 headerentry = ((ComposeHeaderEntry *)list->data);
5087                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5088
5089                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5090                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5091                         Xstrdup_a(str, entstr, return -1);
5092                         g_strstrip(str);
5093                         if (str[0] != '\0') {
5094                                 compose_convert_header
5095                                         (compose, buf, sizeof(buf), str,
5096                                         strlen("Resent-To") + 2, TRUE);
5097
5098                                 if (first_to_address) {
5099                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5100                                         first_to_address = FALSE;
5101                                 } else {
5102                                         err |= (fprintf(fp, ",") < 0);
5103                                 }
5104                                 err |= (fprintf(fp, "%s", buf) < 0);
5105                         }
5106                 }
5107         }
5108         if (!first_to_address) {
5109                 err |= (fprintf(fp, "\n") < 0);
5110         }
5111
5112         first_cc_address = TRUE;
5113         for (list = compose->header_list; list; list = list->next) {
5114                 headerentry = ((ComposeHeaderEntry *)list->data);
5115                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5116
5117                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5118                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5119                         Xstrdup_a(str, strg, return -1);
5120                         g_strstrip(str);
5121                         if (str[0] != '\0') {
5122                                 compose_convert_header
5123                                         (compose, buf, sizeof(buf), str,
5124                                         strlen("Resent-Cc") + 2, TRUE);
5125
5126                                 if (first_cc_address) {
5127                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5128                                         first_cc_address = FALSE;
5129                                 } else {
5130                                         err |= (fprintf(fp, ",") < 0);
5131                                 }
5132                                 err |= (fprintf(fp, "%s", buf) < 0);
5133                         }
5134                 }
5135         }
5136         if (!first_cc_address) {
5137                 err |= (fprintf(fp, "\n") < 0);
5138         }
5139         
5140         return (err ? -1:0);
5141 }
5142
5143 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5144 {
5145         gchar buf[BUFFSIZE];
5146         gchar *str;
5147         const gchar *entstr;
5148         /* struct utsname utsbuf; */
5149         gboolean err = FALSE;
5150
5151         cm_return_val_if_fail(fp != NULL, -1);
5152         cm_return_val_if_fail(compose->account != NULL, -1);
5153         cm_return_val_if_fail(compose->account->address != NULL, -1);
5154
5155         /* Resent-Date */
5156         get_rfc822_date(buf, sizeof(buf));
5157         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5158
5159         /* Resent-From */
5160         if (compose->account->name && *compose->account->name) {
5161                 compose_convert_header
5162                         (compose, buf, sizeof(buf), compose->account->name,
5163                          strlen("From: "), TRUE);
5164                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5165                         buf, compose->account->address) < 0);
5166         } else
5167                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5168
5169         /* Subject */
5170         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5171         if (*entstr != '\0') {
5172                 Xstrdup_a(str, entstr, return -1);
5173                 g_strstrip(str);
5174                 if (*str != '\0') {
5175                         compose_convert_header(compose, buf, sizeof(buf), str,
5176                                                strlen("Subject: "), FALSE);
5177                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5178                 }
5179         }
5180
5181         /* Resent-Message-ID */
5182         if (compose->account->set_domain && compose->account->domain) {
5183                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5184         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5185                 g_snprintf(buf, sizeof(buf), "%s", 
5186                         strchr(compose->account->address, '@') ?
5187                                 strchr(compose->account->address, '@')+1 :
5188                                 compose->account->address);
5189         } else {
5190                 g_snprintf(buf, sizeof(buf), "%s", "");
5191         }
5192
5193         if (compose->account->gen_msgid) {
5194                 gchar *addr = NULL;
5195                 if (compose->account->msgid_with_addr) {
5196                         addr = compose->account->address;
5197                 }
5198                 generate_msgid(buf, sizeof(buf), addr);
5199                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5200                 compose->msgid = g_strdup(buf);
5201         } else {
5202                 compose->msgid = NULL;
5203         }
5204
5205         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5206                 return -1;
5207
5208         /* separator between header and body */
5209         err |= (fputs("\n", fp) == EOF);
5210
5211         return (err ? -1:0);
5212 }
5213
5214 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5215 {
5216         FILE *fp;
5217         size_t len;
5218         gchar buf[BUFFSIZE];
5219         int i = 0;
5220         gboolean skip = FALSE;
5221         gboolean err = FALSE;
5222         gchar *not_included[]={
5223                 "Return-Path:",         "Delivered-To:",        "Received:",
5224                 "Subject:",             "X-UIDL:",              "AF:",
5225                 "NF:",                  "PS:",                  "SRH:",
5226                 "SFN:",                 "DSR:",                 "MID:",
5227                 "CFG:",                 "PT:",                  "S:",
5228                 "RQ:",                  "SSV:",                 "NSV:",
5229                 "SSH:",                 "R:",                   "MAID:",
5230                 "NAID:",                "RMID:",                "FMID:",
5231                 "SCF:",                 "RRCPT:",               "NG:",
5232                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5233                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5234                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5235                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5236                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5237                 NULL
5238                 };
5239         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5240                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5241                 return -1;
5242         }
5243
5244         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5245                 skip = FALSE;
5246                 for (i = 0; not_included[i] != NULL; i++) {
5247                         if (g_ascii_strncasecmp(buf, not_included[i],
5248                                                 strlen(not_included[i])) == 0) {
5249                                 skip = TRUE;
5250                                 break;
5251                         }
5252                 }
5253                 if (skip)
5254                         continue;
5255                 if (fputs(buf, fdest) == -1)
5256                         goto error;
5257
5258                 if (!prefs_common.redirect_keep_from) {
5259                         if (g_ascii_strncasecmp(buf, "From:",
5260                                           strlen("From:")) == 0) {
5261                                 err |= (fputs(" (by way of ", fdest) == EOF);
5262                                 if (compose->account->name
5263                                     && *compose->account->name) {
5264                                         compose_convert_header
5265                                                 (compose, buf, sizeof(buf),
5266                                                  compose->account->name,
5267                                                  strlen("From: "),
5268                                                  FALSE);
5269                                         err |= (fprintf(fdest, "%s <%s>",
5270                                                 buf,
5271                                                 compose->account->address) < 0);
5272                                 } else
5273                                         err |= (fprintf(fdest, "%s",
5274                                                 compose->account->address) < 0);
5275                                 err |= (fputs(")", fdest) == EOF);
5276                         }
5277                 }
5278
5279                 if (fputs("\n", fdest) == -1)
5280                         goto error;
5281         }
5282
5283         if (err)
5284                 goto error;
5285
5286         if (compose_redirect_write_headers(compose, fdest))
5287                 goto error;
5288
5289         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5290                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5291                         goto error;
5292         }
5293
5294         fclose(fp);
5295
5296         return 0;
5297 error:
5298         fclose(fp);
5299
5300         return -1;
5301 }
5302
5303 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5304 {
5305         GtkTextBuffer *buffer;
5306         GtkTextIter start, end;
5307         gchar *chars;
5308         gchar *buf;
5309         const gchar *out_codeset;
5310         EncodingType encoding = ENC_UNKNOWN;
5311         MimeInfo *mimemsg, *mimetext;
5312         gint line;
5313         const gchar *src_codeset = CS_INTERNAL;
5314         gchar *from_addr = NULL;
5315         gchar *from_name = NULL;
5316
5317         if (action == COMPOSE_WRITE_FOR_SEND)
5318                 attach_parts = TRUE;
5319
5320         /* create message MimeInfo */
5321         mimemsg = procmime_mimeinfo_new();
5322         mimemsg->type = MIMETYPE_MESSAGE;
5323         mimemsg->subtype = g_strdup("rfc822");
5324         mimemsg->content = MIMECONTENT_MEM;
5325         mimemsg->tmp = TRUE; /* must free content later */
5326         mimemsg->data.mem = compose_get_header(compose);
5327
5328         /* Create text part MimeInfo */
5329         /* get all composed text */
5330         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5331         gtk_text_buffer_get_start_iter(buffer, &start);
5332         gtk_text_buffer_get_end_iter(buffer, &end);
5333         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5334
5335         out_codeset = conv_get_charset_str(compose->out_encoding);
5336
5337         if (!out_codeset && is_ascii_str(chars)) {
5338                 out_codeset = CS_US_ASCII;
5339         } else if (prefs_common.outgoing_fallback_to_ascii &&
5340                    is_ascii_str(chars)) {
5341                 out_codeset = CS_US_ASCII;
5342                 encoding = ENC_7BIT;
5343         }
5344
5345         if (!out_codeset) {
5346                 gchar *test_conv_global_out = NULL;
5347                 gchar *test_conv_reply = NULL;
5348
5349                 /* automatic mode. be automatic. */
5350                 codeconv_set_strict(TRUE);
5351
5352                 out_codeset = conv_get_outgoing_charset_str();
5353                 if (out_codeset) {
5354                         debug_print("trying to convert to %s\n", out_codeset);
5355                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5356                 }
5357
5358                 if (!test_conv_global_out && compose->orig_charset
5359                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5360                         out_codeset = compose->orig_charset;
5361                         debug_print("failure; trying to convert to %s\n", out_codeset);
5362                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5363                 }
5364
5365                 if (!test_conv_global_out && !test_conv_reply) {
5366                         /* we're lost */
5367                         out_codeset = CS_INTERNAL;
5368                         debug_print("failure; finally using %s\n", out_codeset);
5369                 }
5370                 g_free(test_conv_global_out);
5371                 g_free(test_conv_reply);
5372                 codeconv_set_strict(FALSE);
5373         }
5374
5375         if (encoding == ENC_UNKNOWN) {
5376                 if (prefs_common.encoding_method == CTE_BASE64)
5377                         encoding = ENC_BASE64;
5378                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5379                         encoding = ENC_QUOTED_PRINTABLE;
5380                 else if (prefs_common.encoding_method == CTE_8BIT)
5381                         encoding = ENC_8BIT;
5382                 else
5383                         encoding = procmime_get_encoding_for_charset(out_codeset);
5384         }
5385
5386         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5387                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5388
5389         if (action == COMPOSE_WRITE_FOR_SEND) {
5390                 codeconv_set_strict(TRUE);
5391                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5392                 codeconv_set_strict(FALSE);
5393
5394                 if (!buf) {
5395                         AlertValue aval;
5396                         gchar *msg;
5397
5398                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5399                                                 "to the specified %s charset.\n"
5400                                                 "Send it as %s?"), out_codeset, src_codeset);
5401                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5402                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5403                         g_free(msg);
5404
5405                         if (aval != G_ALERTALTERNATE) {
5406                                 g_free(chars);
5407                                 return -3;
5408                         } else {
5409                                 buf = chars;
5410                                 out_codeset = src_codeset;
5411                                 chars = NULL;
5412                         }
5413                 }
5414         } else {
5415                 buf = chars;
5416                 out_codeset = src_codeset;
5417                 chars = NULL;
5418         }
5419         g_free(chars);
5420
5421         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5422                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5423                     strstr(buf, "\nFrom ") != NULL) {
5424                         encoding = ENC_QUOTED_PRINTABLE;
5425                 }
5426         }
5427
5428         mimetext = procmime_mimeinfo_new();
5429         mimetext->content = MIMECONTENT_MEM;
5430         mimetext->tmp = TRUE; /* must free content later */
5431         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5432          * and free the data, which we need later. */
5433         mimetext->data.mem = g_strdup(buf); 
5434         mimetext->type = MIMETYPE_TEXT;
5435         mimetext->subtype = g_strdup("plain");
5436         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5437                             g_strdup(out_codeset));
5438                             
5439         /* protect trailing spaces when signing message */
5440         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5441             privacy_system_can_sign(compose->privacy_system)) {
5442                 encoding = ENC_QUOTED_PRINTABLE;
5443         }
5444         
5445         debug_print("main text: %zd bytes encoded as %s in %d\n",
5446                 strlen(buf), out_codeset, encoding);
5447
5448         /* check for line length limit */
5449         if (action == COMPOSE_WRITE_FOR_SEND &&
5450             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5451             check_line_length(buf, 1000, &line) < 0) {
5452                 AlertValue aval;
5453                 gchar *msg;
5454
5455                 msg = g_strdup_printf
5456                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5457                            "The contents of the message might be broken on the way to the delivery.\n"
5458                            "\n"
5459                            "Send it anyway?"), line + 1);
5460                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5461                 g_free(msg);
5462                 if (aval != G_ALERTALTERNATE) {
5463                         g_free(buf);
5464                         return -1;
5465                 }
5466         }
5467         
5468         if (encoding != ENC_UNKNOWN)
5469                 procmime_encode_content(mimetext, encoding);
5470
5471         /* append attachment parts */
5472         if (compose_use_attach(compose) && attach_parts) {
5473                 MimeInfo *mimempart;
5474                 gchar *boundary = NULL;
5475                 mimempart = procmime_mimeinfo_new();
5476                 mimempart->content = MIMECONTENT_EMPTY;
5477                 mimempart->type = MIMETYPE_MULTIPART;
5478                 mimempart->subtype = g_strdup("mixed");
5479
5480                 do {
5481                         g_free(boundary);
5482                         boundary = generate_mime_boundary(NULL);
5483                 } while (strstr(buf, boundary) != NULL);
5484
5485                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5486                                     boundary);
5487
5488                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5489
5490                 g_node_append(mimempart->node, mimetext->node);
5491                 g_node_append(mimemsg->node, mimempart->node);
5492
5493                 if (compose_add_attachments(compose, mimempart) < 0)
5494                         return -1;
5495         } else
5496                 g_node_append(mimemsg->node, mimetext->node);
5497
5498         g_free(buf);
5499
5500         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5501                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5502                 /* extract name and address */
5503                 if (strstr(spec, " <") && strstr(spec, ">")) {
5504                         from_addr = g_strdup(strrchr(spec, '<')+1);
5505                         *(strrchr(from_addr, '>')) = '\0';
5506                         from_name = g_strdup(spec);
5507                         *(strrchr(from_name, '<')) = '\0';
5508                 } else {
5509                         from_name = NULL;
5510                         from_addr = NULL;
5511                 }
5512                 g_free(spec);
5513         }
5514         /* sign message if sending */
5515         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5516             privacy_system_can_sign(compose->privacy_system))
5517                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5518                         compose->account, from_addr)) {
5519                         g_free(from_name);
5520                         g_free(from_addr);
5521                         return -2;
5522         }
5523         g_free(from_name);
5524         g_free(from_addr);
5525         procmime_write_mimeinfo(mimemsg, fp);
5526         
5527         procmime_mimeinfo_free_all(mimemsg);
5528
5529         return 0;
5530 }
5531
5532 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5533 {
5534         GtkTextBuffer *buffer;
5535         GtkTextIter start, end;
5536         FILE *fp;
5537         size_t len;
5538         gchar *chars, *tmp;
5539
5540         if ((fp = g_fopen(file, "wb")) == NULL) {
5541                 FILE_OP_ERROR(file, "fopen");
5542                 return -1;
5543         }
5544
5545         /* chmod for security */
5546         if (change_file_mode_rw(fp, file) < 0) {
5547                 FILE_OP_ERROR(file, "chmod");
5548                 g_warning("can't change file mode\n");
5549         }
5550
5551         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5552         gtk_text_buffer_get_start_iter(buffer, &start);
5553         gtk_text_buffer_get_end_iter(buffer, &end);
5554         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5555
5556         chars = conv_codeset_strdup
5557                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5558
5559         g_free(tmp);
5560         if (!chars) return -1;
5561
5562         /* write body */
5563         len = strlen(chars);
5564         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5565                 FILE_OP_ERROR(file, "fwrite");
5566                 g_free(chars);
5567                 fclose(fp);
5568                 claws_unlink(file);
5569                 return -1;
5570         }
5571
5572         g_free(chars);
5573
5574         if (fclose(fp) == EOF) {
5575                 FILE_OP_ERROR(file, "fclose");
5576                 claws_unlink(file);
5577                 return -1;
5578         }
5579         return 0;
5580 }
5581
5582 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5583 {
5584         FolderItem *item;
5585         MsgInfo *msginfo = compose->targetinfo;
5586
5587         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5588         if (!msginfo) return -1;
5589
5590         if (!force && MSG_IS_LOCKED(msginfo->flags))
5591                 return 0;
5592
5593         item = msginfo->folder;
5594         cm_return_val_if_fail(item != NULL, -1);
5595
5596         if (procmsg_msg_exist(msginfo) &&
5597             (folder_has_parent_of_type(item, F_QUEUE) ||
5598              folder_has_parent_of_type(item, F_DRAFT) 
5599              || msginfo == compose->autosaved_draft)) {
5600                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5601                         g_warning("can't remove the old message\n");
5602                         return -1;
5603                 } else {
5604                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5605                 }
5606         }
5607
5608         return 0;
5609 }
5610
5611 static void compose_remove_draft(Compose *compose)
5612 {
5613         FolderItem *drafts;
5614         MsgInfo *msginfo = compose->targetinfo;
5615         drafts = account_get_special_folder(compose->account, F_DRAFT);
5616
5617         if (procmsg_msg_exist(msginfo)) {
5618                 folder_item_remove_msg(drafts, msginfo->msgnum);
5619         }
5620
5621 }
5622
5623 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5624                    gboolean remove_reedit_target)
5625 {
5626         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5627 }
5628
5629 static gboolean compose_warn_encryption(Compose *compose)
5630 {
5631         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5632         AlertValue val = G_ALERTALTERNATE;
5633         
5634         if (warning == NULL)
5635                 return TRUE;
5636
5637         val = alertpanel_full(_("Encryption warning"), warning,
5638                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5639                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5640         if (val & G_ALERTDISABLE) {
5641                 val &= ~G_ALERTDISABLE;
5642                 if (val == G_ALERTALTERNATE)
5643                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5644                                 TRUE);
5645         }
5646
5647         if (val == G_ALERTALTERNATE) {
5648                 return TRUE;
5649         } else {
5650                 return FALSE;
5651         } 
5652 }
5653
5654 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5655                               gchar **msgpath, gboolean check_subject,
5656                               gboolean remove_reedit_target)
5657 {
5658         FolderItem *queue;
5659         gchar *tmp;
5660         FILE *fp;
5661         GSList *cur;
5662         gint num;
5663         static gboolean lock = FALSE;
5664         PrefsAccount *mailac = NULL, *newsac = NULL;
5665         gboolean err = FALSE;
5666
5667         debug_print("queueing message...\n");
5668         cm_return_val_if_fail(compose->account != NULL, -1);
5669
5670         lock = TRUE;
5671         
5672         if (compose_check_entries(compose, check_subject) == FALSE) {
5673                 lock = FALSE;
5674                 if (compose->batch) {
5675                         gtk_widget_show_all(compose->window);
5676                 }
5677                 return -1;
5678         }
5679
5680         if (!compose->to_list && !compose->newsgroup_list) {
5681                 g_warning("can't get recipient list.");
5682                 lock = FALSE;
5683                 return -1;
5684         }
5685
5686         if (compose->to_list) {
5687                 if (compose->account->protocol != A_NNTP)
5688                         mailac = compose->account;
5689                 else if (cur_account && cur_account->protocol != A_NNTP)
5690                         mailac = cur_account;
5691                 else if (!(mailac = compose_current_mail_account())) {
5692                         lock = FALSE;
5693                         alertpanel_error(_("No account for sending mails available!"));
5694                         return -1;
5695                 }
5696         }
5697
5698         if (compose->newsgroup_list) {
5699                 if (compose->account->protocol == A_NNTP)
5700                         newsac = compose->account;
5701                 else {
5702                         lock = FALSE;
5703                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5704                         return -1;
5705                 }                       
5706         }
5707
5708         /* write queue header */
5709         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5710                               G_DIR_SEPARATOR, compose, (guint) rand());
5711         debug_print("queuing to %s\n", tmp);
5712         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5713                 FILE_OP_ERROR(tmp, "fopen");
5714                 g_free(tmp);
5715                 lock = FALSE;
5716                 return -2;
5717         }
5718
5719         if (change_file_mode_rw(fp, tmp) < 0) {
5720                 FILE_OP_ERROR(tmp, "chmod");
5721                 g_warning("can't change file mode\n");
5722         }
5723
5724         /* queueing variables */
5725         err |= (fprintf(fp, "AF:\n") < 0);
5726         err |= (fprintf(fp, "NF:0\n") < 0);
5727         err |= (fprintf(fp, "PS:10\n") < 0);
5728         err |= (fprintf(fp, "SRH:1\n") < 0);
5729         err |= (fprintf(fp, "SFN:\n") < 0);
5730         err |= (fprintf(fp, "DSR:\n") < 0);
5731         if (compose->msgid)
5732                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5733         else
5734                 err |= (fprintf(fp, "MID:\n") < 0);
5735         err |= (fprintf(fp, "CFG:\n") < 0);
5736         err |= (fprintf(fp, "PT:0\n") < 0);
5737         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5738         err |= (fprintf(fp, "RQ:\n") < 0);
5739         if (mailac)
5740                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5741         else
5742                 err |= (fprintf(fp, "SSV:\n") < 0);
5743         if (newsac)
5744                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5745         else
5746                 err |= (fprintf(fp, "NSV:\n") < 0);
5747         err |= (fprintf(fp, "SSH:\n") < 0);
5748         /* write recepient list */
5749         if (compose->to_list) {
5750                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5751                 for (cur = compose->to_list->next; cur != NULL;
5752                      cur = cur->next)
5753                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5754                 err |= (fprintf(fp, "\n") < 0);
5755         }
5756         /* write newsgroup list */
5757         if (compose->newsgroup_list) {
5758                 err |= (fprintf(fp, "NG:") < 0);
5759                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5760                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5761                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5762                 err |= (fprintf(fp, "\n") < 0);
5763         }
5764         /* Sylpheed account IDs */
5765         if (mailac)
5766                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5767         if (newsac)
5768                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5769
5770         
5771         if (compose->privacy_system != NULL) {
5772                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5773                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5774                 if (compose->use_encryption) {
5775                         gchar *encdata;
5776                         if (!compose_warn_encryption(compose)) {
5777                                 lock = FALSE;
5778                                 fclose(fp);
5779                                 claws_unlink(tmp);
5780                                 g_free(tmp);
5781                                 return -6;
5782                         }
5783                         if (mailac && mailac->encrypt_to_self) {
5784                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5785                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5786                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5787                                 g_slist_free(tmp_list);
5788                         } else {
5789                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5790                         }
5791                         if (encdata != NULL) {
5792                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5793                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5794                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5795                                                 encdata) < 0);
5796                                 } /* else we finally dont want to encrypt */
5797                         } else {
5798                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5799                                 /* and if encdata was null, it means there's been a problem in 
5800                                  * key selection */
5801                                 lock = FALSE;
5802                                 fclose(fp);
5803                                 claws_unlink(tmp);
5804                                 g_free(tmp);
5805                                 return -5;
5806                         }
5807                         g_free(encdata);
5808                 }
5809         }
5810
5811         /* Save copy folder */
5812         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5813                 gchar *savefolderid;
5814                 
5815                 savefolderid = compose_get_save_to(compose);
5816                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5817                 g_free(savefolderid);
5818         }
5819         /* Save copy folder */
5820         if (compose->return_receipt) {
5821                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5822         }
5823         /* Message-ID of message replying to */
5824         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5825                 gchar *folderid;
5826                 
5827                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5828                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5829                 g_free(folderid);
5830         }
5831         /* Message-ID of message forwarding to */
5832         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5833                 gchar *folderid;
5834                 
5835                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5836                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5837                 g_free(folderid);
5838         }
5839
5840         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5841         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5842
5843         /* end of headers */
5844         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5845
5846         if (compose->redirect_filename != NULL) {
5847                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5848                         lock = FALSE;
5849                         fclose(fp);
5850                         claws_unlink(tmp);
5851                         g_free(tmp);
5852                         return -2;
5853                 }
5854         } else {
5855                 gint result = 0;
5856                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5857                         lock = FALSE;
5858                         fclose(fp);
5859                         claws_unlink(tmp);
5860                         g_free(tmp);
5861                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5862                 }
5863         }
5864         if (err == TRUE) {
5865                 g_warning("failed to write queue message\n");
5866                 fclose(fp);
5867                 claws_unlink(tmp);
5868                 g_free(tmp);
5869                 lock = FALSE;
5870                 return -2;
5871         }
5872         if (fclose(fp) == EOF) {
5873                 FILE_OP_ERROR(tmp, "fclose");
5874                 claws_unlink(tmp);
5875                 g_free(tmp);
5876                 lock = FALSE;
5877                 return -2;
5878         }
5879
5880         if (item && *item) {
5881                 queue = *item;
5882         } else {
5883                 queue = account_get_special_folder(compose->account, F_QUEUE);
5884         }
5885         if (!queue) {
5886                 g_warning("can't find queue folder\n");
5887                 claws_unlink(tmp);
5888                 g_free(tmp);
5889                 lock = FALSE;
5890                 return -1;
5891         }
5892         folder_item_scan(queue);
5893         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5894                 g_warning("can't queue the message\n");
5895                 claws_unlink(tmp);
5896                 g_free(tmp);
5897                 lock = FALSE;
5898                 return -1;
5899         }
5900         
5901         if (msgpath == NULL) {
5902                 claws_unlink(tmp);
5903                 g_free(tmp);
5904         } else
5905                 *msgpath = tmp;
5906
5907         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5908                 compose_remove_reedit_target(compose, FALSE);
5909         }
5910
5911         if ((msgnum != NULL) && (item != NULL)) {
5912                 *msgnum = num;
5913                 *item = queue;
5914         }
5915
5916         return 0;
5917 }
5918
5919 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5920 {
5921         AttachInfo *ainfo;
5922         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5923         MimeInfo *mimepart;
5924         struct stat statbuf;
5925         gchar *type, *subtype;
5926         GtkTreeModel *model;
5927         GtkTreeIter iter;
5928
5929         model = gtk_tree_view_get_model(tree_view);
5930         
5931         if (!gtk_tree_model_get_iter_first(model, &iter))
5932                 return 0;
5933         do {
5934                 gtk_tree_model_get(model, &iter,
5935                                    COL_DATA, &ainfo,
5936                                    -1);
5937                 
5938                 if (!is_file_exist(ainfo->file)) {
5939                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5940                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5941                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5942                         g_free(msg);
5943                         if (val == G_ALERTDEFAULT) {
5944                                 return -1;
5945                         }
5946                         continue;
5947                 }
5948                 mimepart = procmime_mimeinfo_new();
5949                 mimepart->content = MIMECONTENT_FILE;
5950                 mimepart->data.filename = g_strdup(ainfo->file);
5951                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5952                 mimepart->offset = 0;
5953
5954                 g_stat(ainfo->file, &statbuf);
5955                 mimepart->length = statbuf.st_size;
5956
5957                 type = g_strdup(ainfo->content_type);
5958
5959                 if (!strchr(type, '/')) {
5960                         g_free(type);
5961                         type = g_strdup("application/octet-stream");
5962                 }
5963
5964                 subtype = strchr(type, '/') + 1;
5965                 *(subtype - 1) = '\0';
5966                 mimepart->type = procmime_get_media_type(type);
5967                 mimepart->subtype = g_strdup(subtype);
5968                 g_free(type);
5969
5970                 if (mimepart->type == MIMETYPE_MESSAGE && 
5971                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5972                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5973                 } else {
5974                         if (ainfo->name) {
5975                                 if (mimepart->type == MIMETYPE_APPLICATION && 
5976                                    !strcmp2(mimepart->subtype, "octet-stream"))
5977                                         g_hash_table_insert(mimepart->typeparameters,
5978                                                     g_strdup("name"), g_strdup(ainfo->name));
5979                                 g_hash_table_insert(mimepart->dispositionparameters,
5980                                             g_strdup("filename"), g_strdup(ainfo->name));
5981                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5982                         }
5983                 }
5984
5985                 if (compose->use_signing) {
5986                         if (ainfo->encoding == ENC_7BIT)
5987                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5988                         else if (ainfo->encoding == ENC_8BIT)
5989                                 ainfo->encoding = ENC_BASE64;
5990                 }
5991                 
5992                 procmime_encode_content(mimepart, ainfo->encoding);
5993
5994                 g_node_append(parent->node, mimepart->node);
5995         } while (gtk_tree_model_iter_next(model, &iter));
5996         
5997         return 0;
5998 }
5999
6000 #define IS_IN_CUSTOM_HEADER(header) \
6001         (compose->account->add_customhdr && \
6002          custom_header_find(compose->account->customhdr_list, header) != NULL)
6003
6004 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6005                                                     GString *header, 
6006                                                     const gchar *fieldname,
6007                                                     const gchar *seperator)
6008 {
6009         gchar *str, *fieldname_w_colon;
6010         gboolean add_field = FALSE;
6011         GSList *list;
6012         ComposeHeaderEntry *headerentry;
6013         const gchar *headerentryname;
6014         const gchar *trans_fieldname;
6015         GString *fieldstr;
6016
6017         if (IS_IN_CUSTOM_HEADER(fieldname))
6018                 return;
6019
6020         debug_print("Adding %s-fields\n", fieldname);
6021
6022         fieldstr = g_string_sized_new(64);
6023
6024         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6025         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6026
6027         for (list = compose->header_list; list; list = list->next) {
6028                 headerentry = ((ComposeHeaderEntry *)list->data);
6029                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6030
6031                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6032                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6033                         g_strstrip(str);
6034                         if (str[0] != '\0') {
6035                                 if (add_field)
6036                                         g_string_append(fieldstr, seperator);
6037                                 g_string_append(fieldstr, str);
6038                                 add_field = TRUE;
6039                         }
6040                         g_free(str);
6041                 }
6042         }
6043         if (add_field) {
6044                 gchar *buf;
6045
6046                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6047                 compose_convert_header
6048                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6049                         strlen(fieldname) + 2, TRUE);
6050                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6051                 g_free(buf);
6052         }
6053
6054         g_free(fieldname_w_colon);
6055         g_string_free(fieldstr, TRUE);
6056
6057         return;
6058 }
6059
6060 static gchar *compose_get_header(Compose *compose)
6061 {
6062         gchar buf[BUFFSIZE];
6063         const gchar *entry_str;
6064         gchar *str;
6065         gchar *name;
6066         GSList *list;
6067         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6068         GString *header;
6069         gchar *from_name = NULL, *from_address = NULL;
6070         gchar *tmp;
6071
6072         cm_return_val_if_fail(compose->account != NULL, NULL);
6073         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6074
6075         header = g_string_sized_new(64);
6076
6077         /* Date */
6078         get_rfc822_date(buf, sizeof(buf));
6079         g_string_append_printf(header, "Date: %s\n", buf);
6080
6081         /* From */
6082         
6083         if (compose->account->name && *compose->account->name) {
6084                 gchar *buf;
6085                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6086                 tmp = g_strdup_printf("%s <%s>",
6087                         buf, compose->account->address);
6088         } else {
6089                 tmp = g_strdup_printf("%s",
6090                         compose->account->address);
6091         }
6092         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6093         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6094                 /* use default */
6095                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6096                 from_address = g_strdup(compose->account->address);
6097         } else {
6098                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6099                 /* extract name and address */
6100                 if (strstr(spec, " <") && strstr(spec, ">")) {
6101                         from_address = g_strdup(strrchr(spec, '<')+1);
6102                         *(strrchr(from_address, '>')) = '\0';
6103                         from_name = g_strdup(spec);
6104                         *(strrchr(from_name, '<')) = '\0';
6105                 } else {
6106                         from_name = NULL;
6107                         from_address = g_strdup(spec);
6108                 }
6109                 g_free(spec);
6110         }
6111         g_free(tmp);
6112         
6113         
6114         if (from_name && *from_name) {
6115                 compose_convert_header
6116                         (compose, buf, sizeof(buf), from_name,
6117                          strlen("From: "), TRUE);
6118                 QUOTE_IF_REQUIRED(name, buf);
6119                 
6120                 g_string_append_printf(header, "From: %s <%s>\n",
6121                         name, from_address);
6122         } else
6123                 g_string_append_printf(header, "From: %s\n", from_address);
6124         
6125         g_free(from_name);
6126         g_free(from_address);
6127
6128         /* To */
6129         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6130
6131         /* Newsgroups */
6132         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6133
6134         /* Cc */
6135         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6136
6137         /* Bcc */
6138         /* 
6139          * If this account is a NNTP account remove Bcc header from 
6140          * message body since it otherwise will be publicly shown
6141          */
6142         if (compose->account->protocol != A_NNTP)
6143                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6144
6145         /* Subject */
6146         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6147
6148         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6149                 g_strstrip(str);
6150                 if (*str != '\0') {
6151                         compose_convert_header(compose, buf, sizeof(buf), str,
6152                                                strlen("Subject: "), FALSE);
6153                         g_string_append_printf(header, "Subject: %s\n", buf);
6154                 }
6155         }
6156         g_free(str);
6157
6158         /* Message-ID */
6159         if (compose->account->set_domain && compose->account->domain) {
6160                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
6161         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6162                 g_snprintf(buf, sizeof(buf), "%s", 
6163                         strchr(compose->account->address, '@') ?
6164                                 strchr(compose->account->address, '@')+1 :
6165                                 compose->account->address);
6166         } else {
6167                 g_snprintf(buf, sizeof(buf), "%s", "");
6168         }
6169         
6170         if (compose->account->gen_msgid) {
6171                 gchar *addr = NULL;
6172                 if (compose->account->msgid_with_addr) {
6173                         addr = compose->account->address;
6174                 }
6175                 generate_msgid(buf, sizeof(buf), addr);
6176                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6177                 compose->msgid = g_strdup(buf);
6178         } else {
6179                 compose->msgid = NULL;
6180         }
6181
6182         if (compose->remove_references == FALSE) {
6183                 /* In-Reply-To */
6184                 if (compose->inreplyto && compose->to_list)
6185                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6186         
6187                 /* References */
6188                 if (compose->references)
6189                         g_string_append_printf(header, "References: %s\n", compose->references);
6190         }
6191
6192         /* Followup-To */
6193         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6194
6195         /* Reply-To */
6196         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6197
6198         /* Organization */
6199         if (compose->account->organization &&
6200             strlen(compose->account->organization) &&
6201             !IS_IN_CUSTOM_HEADER("Organization")) {
6202                 compose_convert_header(compose, buf, sizeof(buf),
6203                                        compose->account->organization,
6204                                        strlen("Organization: "), FALSE);
6205                 g_string_append_printf(header, "Organization: %s\n", buf);
6206         }
6207
6208         /* Program version and system info */
6209         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6210             !compose->newsgroup_list) {
6211                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6212                         prog_version,
6213                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6214                         TARGET_ALIAS);
6215         }
6216         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6217                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6218                         prog_version,
6219                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6220                         TARGET_ALIAS);
6221         }
6222
6223         /* custom headers */
6224         if (compose->account->add_customhdr) {
6225                 GSList *cur;
6226
6227                 for (cur = compose->account->customhdr_list; cur != NULL;
6228                      cur = cur->next) {
6229                         CustomHeader *chdr = (CustomHeader *)cur->data;
6230
6231                         if (custom_header_is_allowed(chdr->name)) {
6232                                 compose_convert_header
6233                                         (compose, buf, sizeof(buf),
6234                                          chdr->value ? chdr->value : "",
6235                                          strlen(chdr->name) + 2, FALSE);
6236                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6237                         }
6238                 }
6239         }
6240
6241         /* Automatic Faces and X-Faces */
6242         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6243                 g_string_append_printf(header, "X-Face: %s\n", buf);
6244         }
6245         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6246                 g_string_append_printf(header, "X-Face: %s\n", buf);
6247         }
6248         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6249                 g_string_append_printf(header, "Face: %s\n", buf);
6250         }
6251         else if (get_default_face (buf, sizeof(buf)) == 0) {
6252                 g_string_append_printf(header, "Face: %s\n", buf);
6253         }
6254
6255         /* PRIORITY */
6256         switch (compose->priority) {
6257                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6258                                                    "X-Priority: 1 (Highest)\n");
6259                         break;
6260                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6261                                                 "X-Priority: 2 (High)\n");
6262                         break;
6263                 case PRIORITY_NORMAL: break;
6264                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6265                                                "X-Priority: 4 (Low)\n");
6266                         break;
6267                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6268                                                   "X-Priority: 5 (Lowest)\n");
6269                         break;
6270                 default: debug_print("compose: priority unknown : %d\n",
6271                                      compose->priority);
6272         }
6273
6274         /* Request Return Receipt */
6275         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6276                 if (compose->return_receipt) {
6277                         if (compose->account->name
6278                             && *compose->account->name) {
6279                                 compose_convert_header(compose, buf, sizeof(buf), 
6280                                                        compose->account->name, 
6281                                                        strlen("Disposition-Notification-To: "),
6282                                                        TRUE);
6283                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6284                         } else
6285                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6286                 }
6287         }
6288
6289         /* get special headers */
6290         for (list = compose->header_list; list; list = list->next) {
6291                 ComposeHeaderEntry *headerentry;
6292                 gchar *tmp;
6293                 gchar *headername;
6294                 gchar *headername_wcolon;
6295                 const gchar *headername_trans;
6296                 gchar *headervalue;
6297                 gchar **string;
6298                 gboolean standard_header = FALSE;
6299
6300                 headerentry = ((ComposeHeaderEntry *)list->data);
6301
6302                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6303                 g_strstrip(tmp);
6304                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6305                         g_free(tmp);
6306                         continue;
6307                 }
6308
6309                 if (!strstr(tmp, ":")) {
6310                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6311                         headername = g_strdup(tmp);
6312                 } else {
6313                         headername_wcolon = g_strdup(tmp);
6314                         headername = g_strdup(strtok(tmp, ":"));
6315                 }
6316                 g_free(tmp);
6317                 
6318                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6319                 Xstrdup_a(headervalue, entry_str, return NULL);
6320                 subst_char(headervalue, '\r', ' ');
6321                 subst_char(headervalue, '\n', ' ');
6322                 string = std_headers;
6323                 while (*string != NULL) {
6324                         headername_trans = prefs_common_translated_header_name(*string);
6325                         if (!strcmp(headername_trans, headername_wcolon))
6326                                 standard_header = TRUE;
6327                         string++;
6328                 }
6329                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6330                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6331                                 
6332                 g_free(headername);
6333                 g_free(headername_wcolon);              
6334         }
6335
6336         str = header->str;
6337         g_string_free(header, FALSE);
6338
6339         return str;
6340 }
6341
6342 #undef IS_IN_CUSTOM_HEADER
6343
6344 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6345                                    gint header_len, gboolean addr_field)
6346 {
6347         gchar *tmpstr = NULL;
6348         const gchar *out_codeset = NULL;
6349
6350         cm_return_if_fail(src != NULL);
6351         cm_return_if_fail(dest != NULL);
6352
6353         if (len < 1) return;
6354
6355         tmpstr = g_strdup(src);
6356
6357         subst_char(tmpstr, '\n', ' ');
6358         subst_char(tmpstr, '\r', ' ');
6359         g_strchomp(tmpstr);
6360
6361         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6362                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6363                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6364                 g_free(tmpstr);
6365                 tmpstr = mybuf;
6366         }
6367
6368         codeconv_set_strict(TRUE);
6369         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6370                 conv_get_charset_str(compose->out_encoding));
6371         codeconv_set_strict(FALSE);
6372         
6373         if (!dest || *dest == '\0') {
6374                 gchar *test_conv_global_out = NULL;
6375                 gchar *test_conv_reply = NULL;
6376
6377                 /* automatic mode. be automatic. */
6378                 codeconv_set_strict(TRUE);
6379
6380                 out_codeset = conv_get_outgoing_charset_str();
6381                 if (out_codeset) {
6382                         debug_print("trying to convert to %s\n", out_codeset);
6383                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6384                 }
6385
6386                 if (!test_conv_global_out && compose->orig_charset
6387                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6388                         out_codeset = compose->orig_charset;
6389                         debug_print("failure; trying to convert to %s\n", out_codeset);
6390                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6391                 }
6392
6393                 if (!test_conv_global_out && !test_conv_reply) {
6394                         /* we're lost */
6395                         out_codeset = CS_INTERNAL;
6396                         debug_print("finally using %s\n", out_codeset);
6397                 }
6398                 g_free(test_conv_global_out);
6399                 g_free(test_conv_reply);
6400                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6401                                         out_codeset);
6402                 codeconv_set_strict(FALSE);
6403         }
6404         g_free(tmpstr);
6405 }
6406
6407 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6408 {
6409         gchar *address;
6410
6411         cm_return_if_fail(user_data != NULL);
6412
6413         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6414         g_strstrip(address);
6415         if (*address != '\0') {
6416                 gchar *name = procheader_get_fromname(address);
6417                 extract_address(address);
6418                 addressbook_add_contact(name, address, NULL, NULL);
6419         }
6420         g_free(address);
6421 }
6422
6423 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6424 {
6425         GtkWidget *menuitem;
6426         gchar *address;
6427
6428         cm_return_if_fail(menu != NULL);
6429         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6430
6431         menuitem = gtk_separator_menu_item_new();
6432         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6433         gtk_widget_show(menuitem);
6434
6435         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6436         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6437
6438         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6439         g_strstrip(address);
6440         if (*address == '\0') {
6441                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6442         }
6443
6444         g_signal_connect(G_OBJECT(menuitem), "activate",
6445                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6446         gtk_widget_show(menuitem);
6447 }
6448
6449 static void compose_create_header_entry(Compose *compose) 
6450 {
6451         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6452
6453         GtkWidget *combo;
6454         GtkWidget *entry;
6455         GtkWidget *button;
6456         GtkWidget *hbox;
6457         gchar **string;
6458         const gchar *header = NULL;
6459         ComposeHeaderEntry *headerentry;
6460         gboolean standard_header = FALSE;
6461         GtkListStore *model;
6462         GtkTreeIter iter;
6463 #if !(GTK_CHECK_VERSION(2,12,0))
6464         GtkTooltips *tips = compose->tooltips;
6465 #endif
6466         
6467         headerentry = g_new0(ComposeHeaderEntry, 1);
6468
6469         /* Combo box */
6470         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6471         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6472         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6473                         COMPOSE_TO);
6474         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6475                         COMPOSE_CC);
6476         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6477                         COMPOSE_BCC);
6478         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6479                         COMPOSE_NEWSGROUPS);                    
6480         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6481                         COMPOSE_REPLYTO);
6482         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6483                         COMPOSE_FOLLOWUPTO);
6484
6485         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6486         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6487                          G_CALLBACK(compose_grab_focus_cb), compose);
6488         gtk_widget_show(combo);
6489         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6490                         compose->header_nextrow, compose->header_nextrow+1,
6491                         GTK_SHRINK, GTK_FILL, 0, 0);
6492         if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6493                 const gchar *last_header_entry = gtk_entry_get_text(
6494                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6495                 string = headers;
6496                 while (*string != NULL) {
6497                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6498                                 standard_header = TRUE;
6499                         string++;
6500                 }
6501                 if (standard_header)
6502                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6503         }
6504         if (!compose->header_last || !standard_header) {
6505                 switch(compose->account->protocol) {
6506                         case A_NNTP:
6507                                 header = prefs_common_translated_header_name("Newsgroups:");
6508                                 break;
6509                         default:
6510                                 header = prefs_common_translated_header_name("To:");
6511                                 break;
6512                 }                                                                   
6513         }
6514         if (header)
6515                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6516
6517         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6518                          G_CALLBACK(compose_grab_focus_cb), compose);
6519
6520         /* Entry field with cleanup button */
6521         button = gtk_button_new();
6522         gtk_button_set_image(GTK_BUTTON(button),
6523                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6524         gtk_widget_show(button);
6525         CLAWS_SET_TIP(button,
6526                 _("Delete entry contents"));
6527         entry = gtk_entry_new(); 
6528         gtk_widget_show(entry);
6529         CLAWS_SET_TIP(entry,
6530                 _("Use <tab> to autocomplete from addressbook"));
6531         hbox = gtk_hbox_new (FALSE, 0);
6532         gtk_widget_show(hbox);
6533         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6534         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6535         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6536                         compose->header_nextrow, compose->header_nextrow+1,
6537                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6538
6539         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6540                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6541                          headerentry);
6542         g_signal_connect(G_OBJECT(entry), "changed", 
6543                          G_CALLBACK(compose_headerentry_changed_cb), 
6544                          headerentry);
6545         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6546                          G_CALLBACK(compose_grab_focus_cb), compose);
6547
6548         g_signal_connect(G_OBJECT(button), "clicked",
6549                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6550                          headerentry); 
6551                          
6552         /* email dnd */
6553         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6554                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6555                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6556         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6557                          G_CALLBACK(compose_header_drag_received_cb),
6558                          entry);
6559         g_signal_connect(G_OBJECT(entry), "drag-drop",
6560                          G_CALLBACK(compose_drag_drop),
6561                          compose);
6562         g_signal_connect(G_OBJECT(entry), "populate-popup",
6563                          G_CALLBACK(compose_entry_popup_extend),
6564                          NULL);
6565         
6566         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6567
6568         headerentry->compose = compose;
6569         headerentry->combo = combo;
6570         headerentry->entry = entry;
6571         headerentry->button = button;
6572         headerentry->hbox = hbox;
6573         headerentry->headernum = compose->header_nextrow;
6574         headerentry->type = PREF_NONE;
6575
6576         compose->header_nextrow++;
6577         compose->header_last = headerentry;             
6578         compose->header_list =
6579                 g_slist_append(compose->header_list,
6580                                headerentry);
6581 }
6582
6583 static void compose_add_header_entry(Compose *compose, const gchar *header,
6584                                 gchar *text, ComposePrefType pref_type) 
6585 {
6586         ComposeHeaderEntry *last_header = compose->header_last;
6587         gchar *tmp = g_strdup(text), *email;
6588         gboolean replyto_hdr;
6589         
6590         replyto_hdr = (!strcasecmp(header,
6591                                 prefs_common_translated_header_name("Reply-To:")) ||
6592                         !strcasecmp(header,
6593                                 prefs_common_translated_header_name("Followup-To:")) ||
6594                         !strcasecmp(header,
6595                                 prefs_common_translated_header_name("In-Reply-To:")));
6596                 
6597         extract_address(tmp);
6598         email = g_utf8_strdown(tmp, -1);
6599         
6600         if (replyto_hdr == FALSE &&
6601             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6602         {
6603                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6604                                 header, text, (gint) pref_type);
6605                 g_free(email);
6606                 g_free(tmp);
6607                 return;
6608         }
6609         
6610         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6611                 gtk_entry_set_text(GTK_ENTRY(
6612                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6613         else
6614                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6615         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6616         last_header->type = pref_type;
6617
6618         if (replyto_hdr == FALSE)
6619                 g_hash_table_insert(compose->email_hashtable, email,
6620                                     GUINT_TO_POINTER(1));
6621         else
6622                 g_free(email);
6623         
6624         g_free(tmp);
6625 }
6626
6627 static void compose_destroy_headerentry(Compose *compose, 
6628                                         ComposeHeaderEntry *headerentry)
6629 {
6630         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6631         gchar *email;
6632
6633         extract_address(text);
6634         email = g_utf8_strdown(text, -1);
6635         g_hash_table_remove(compose->email_hashtable, email);
6636         g_free(text);
6637         g_free(email);
6638         
6639         gtk_widget_destroy(headerentry->combo);
6640         gtk_widget_destroy(headerentry->entry);
6641         gtk_widget_destroy(headerentry->button);
6642         gtk_widget_destroy(headerentry->hbox);
6643         g_free(headerentry);
6644 }
6645
6646 static void compose_remove_header_entries(Compose *compose) 
6647 {
6648         GSList *list;
6649         for (list = compose->header_list; list; list = list->next)
6650                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6651
6652         compose->header_last = NULL;
6653         g_slist_free(compose->header_list);
6654         compose->header_list = NULL;
6655         compose->header_nextrow = 1;
6656         compose_create_header_entry(compose);
6657 }
6658
6659 static GtkWidget *compose_create_header(Compose *compose) 
6660 {
6661         GtkWidget *from_optmenu_hbox;
6662         GtkWidget *header_scrolledwin;
6663         GtkWidget *header_table;
6664
6665         gint count = 0;
6666
6667         /* header labels and entries */
6668         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6669         gtk_widget_show(header_scrolledwin);
6670         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6671
6672         header_table = gtk_table_new(2, 2, FALSE);
6673         gtk_widget_show(header_table);
6674         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6675         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6676         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6677         count = 0;
6678
6679         /* option menu for selecting accounts */
6680         from_optmenu_hbox = compose_account_option_menu_create(compose);
6681         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6682                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6683         count++;
6684
6685         compose->header_table = header_table;
6686         compose->header_list = NULL;
6687         compose->header_nextrow = count;
6688
6689         compose_create_header_entry(compose);
6690
6691         compose->table            = NULL;
6692
6693         return header_scrolledwin ;
6694 }
6695
6696 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6697 {
6698         Compose *compose = (Compose *)data;
6699         GdkEventButton event;
6700         
6701         event.button = 3;
6702         event.time = gtk_get_current_event_time();
6703
6704         return attach_button_pressed(compose->attach_clist, &event, compose);
6705 }
6706
6707 static GtkWidget *compose_create_attach(Compose *compose)
6708 {
6709         GtkWidget *attach_scrwin;
6710         GtkWidget *attach_clist;
6711
6712         GtkListStore *store;
6713         GtkCellRenderer *renderer;
6714         GtkTreeViewColumn *column;
6715         GtkTreeSelection *selection;
6716
6717         /* attachment list */
6718         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6719         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6720                                        GTK_POLICY_AUTOMATIC,
6721                                        GTK_POLICY_AUTOMATIC);
6722         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6723
6724         store = gtk_list_store_new(N_ATTACH_COLS, 
6725                                    G_TYPE_STRING,
6726                                    G_TYPE_STRING,
6727                                    G_TYPE_STRING,
6728                                    G_TYPE_POINTER,
6729                                    G_TYPE_AUTO_POINTER,
6730                                    -1);
6731         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6732                                         (GTK_TREE_MODEL(store)));
6733         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6734         g_object_unref(store);
6735         
6736         renderer = gtk_cell_renderer_text_new();
6737         column = gtk_tree_view_column_new_with_attributes
6738                         (_("Mime type"), renderer, "text", 
6739                          COL_MIMETYPE, NULL);
6740         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6741         
6742         renderer = gtk_cell_renderer_text_new();
6743         column = gtk_tree_view_column_new_with_attributes
6744                         (_("Size"), renderer, "text", 
6745                          COL_SIZE, NULL);
6746         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6747         
6748         renderer = gtk_cell_renderer_text_new();
6749         column = gtk_tree_view_column_new_with_attributes
6750                         (_("Name"), renderer, "text", 
6751                          COL_NAME, NULL);
6752         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6753
6754         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6755                                      prefs_common.use_stripes_everywhere);
6756         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6757         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6758
6759         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6760                          G_CALLBACK(attach_selected), compose);
6761         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6762                          G_CALLBACK(attach_button_pressed), compose);
6763 #ifndef MAEMO
6764         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6765                          G_CALLBACK(popup_attach_button_pressed), compose);
6766 #else
6767         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6768                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6769         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6770                          G_CALLBACK(popup_attach_button_pressed), compose);
6771 #endif
6772         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6773                          G_CALLBACK(attach_key_pressed), compose);
6774
6775         /* drag and drop */
6776         gtk_drag_dest_set(attach_clist,
6777                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6778                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6779                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6780         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6781                          G_CALLBACK(compose_attach_drag_received_cb),
6782                          compose);
6783         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6784                          G_CALLBACK(compose_drag_drop),
6785                          compose);
6786
6787         compose->attach_scrwin = attach_scrwin;
6788         compose->attach_clist  = attach_clist;
6789
6790         return attach_scrwin;
6791 }
6792
6793 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6794 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6795
6796 static GtkWidget *compose_create_others(Compose *compose)
6797 {
6798         GtkWidget *table;
6799         GtkWidget *savemsg_checkbtn;
6800         GtkWidget *savemsg_combo;
6801         GtkWidget *savemsg_select;
6802         
6803         guint rowcount = 0;
6804         gchar *folderidentifier;
6805
6806         /* Table for settings */
6807         table = gtk_table_new(3, 1, FALSE);
6808         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6809         gtk_widget_show(table);
6810         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6811         rowcount = 0;
6812
6813         /* Save Message to folder */
6814         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6815         gtk_widget_show(savemsg_checkbtn);
6816         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6817         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6818                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6819         }
6820         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6821                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6822
6823         savemsg_combo = gtk_combo_box_entry_new_text();
6824         compose->savemsg_checkbtn = savemsg_checkbtn;
6825         compose->savemsg_combo = savemsg_combo;
6826         gtk_widget_show(savemsg_combo);
6827
6828         if (prefs_common.compose_save_to_history)
6829                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6830                                 prefs_common.compose_save_to_history);
6831
6832         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6833         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6834         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6835                          G_CALLBACK(compose_grab_focus_cb), compose);
6836         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6837                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6838                                   (compose->account, F_OUTBOX));
6839                 compose_set_save_to(compose, folderidentifier);
6840                 g_free(folderidentifier);
6841         }
6842
6843         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6844         gtk_widget_show(savemsg_select);
6845         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6846         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6847                          G_CALLBACK(compose_savemsg_select_cb),
6848                          compose);
6849
6850         rowcount++;
6851
6852         return table;   
6853 }
6854
6855 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6856 {
6857         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6858                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6859 }
6860
6861 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6862 {
6863         FolderItem *dest;
6864         gchar * path;
6865
6866         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6867         if (!dest) return;
6868
6869         path = folder_item_get_identifier(dest);
6870
6871         compose_set_save_to(compose, path);
6872         g_free(path);
6873 }
6874
6875 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6876                                   GdkAtom clip, GtkTextIter *insert_place);
6877
6878
6879 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6880                                        Compose *compose)
6881 {
6882         gint prev_autowrap;
6883         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6884 #if USE_ENCHANT
6885         if (event->button == 3) {
6886                 GtkTextIter iter;
6887                 GtkTextIter sel_start, sel_end;
6888                 gboolean stuff_selected;
6889                 gint x, y;
6890                 /* move the cursor to allow GtkAspell to check the word
6891                  * under the mouse */
6892                 if (event->x && event->y) {
6893                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6894                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6895                                 &x, &y);
6896                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6897                                 &iter, x, y);
6898                 } else {
6899                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6900                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6901                 }
6902                 /* get selection */
6903                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6904                                 buffer,
6905                                 &sel_start, &sel_end);
6906
6907                 gtk_text_buffer_place_cursor (buffer, &iter);
6908                 /* reselect stuff */
6909                 if (stuff_selected 
6910                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6911                         gtk_text_buffer_select_range(buffer,
6912                                 &sel_start, &sel_end);
6913                 }
6914                 return FALSE; /* pass the event so that the right-click goes through */
6915         }
6916 #endif
6917         if (event->button == 2) {
6918                 GtkTextIter iter;
6919                 gint x, y;
6920                 BLOCK_WRAP();
6921                 
6922                 /* get the middle-click position to paste at the correct place */
6923                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6924                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6925                         &x, &y);
6926                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6927                         &iter, x, y);
6928                 
6929                 entry_paste_clipboard(compose, text, 
6930                                 prefs_common.linewrap_pastes,
6931                                 GDK_SELECTION_PRIMARY, &iter);
6932                 UNBLOCK_WRAP();
6933                 return TRUE;
6934         }
6935         return FALSE;
6936 }
6937
6938 #if USE_ENCHANT
6939 static void compose_spell_menu_changed(void *data)
6940 {
6941         Compose *compose = (Compose *)data;
6942         GSList *items;
6943         GtkWidget *menuitem;
6944         GtkWidget *parent_item;
6945         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6946         GSList *spell_menu;
6947
6948         if (compose->gtkaspell == NULL)
6949                 return;
6950
6951         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
6952                         "/Menu/Spelling/Options");
6953
6954         /* setting the submenu removes /Spelling/Options from the factory 
6955          * so we need to save it */
6956
6957         if (parent_item == NULL) {
6958                 parent_item = compose->aspell_options_menu;
6959                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6960         } else
6961                 compose->aspell_options_menu = parent_item;
6962
6963         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6964
6965         spell_menu = g_slist_reverse(spell_menu);
6966         for (items = spell_menu;
6967              items; items = items->next) {
6968                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6969                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6970                 gtk_widget_show(GTK_WIDGET(menuitem));
6971         }
6972         g_slist_free(spell_menu);
6973
6974         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6975         gtk_widget_show(parent_item);
6976 }
6977
6978 static void compose_dict_changed(void *data)
6979 {
6980         Compose *compose = (Compose *) data;
6981
6982         if(compose->gtkaspell && 
6983            compose->gtkaspell->recheck_when_changing_dict == FALSE)
6984                 return;
6985
6986         gtkaspell_highlight_all(compose->gtkaspell);
6987         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6988 }
6989 #endif
6990
6991 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6992 {
6993         Compose *compose = (Compose *)data;
6994         GdkEventButton event;
6995         
6996         event.button = 3;
6997         event.time = gtk_get_current_event_time();
6998         event.x = 0;
6999         event.y = 0;
7000
7001         return text_clicked(compose->text, &event, compose);
7002 }
7003
7004 static gboolean compose_force_window_origin = TRUE;
7005 static Compose *compose_create(PrefsAccount *account,
7006                                                  FolderItem *folder,
7007                                                  ComposeMode mode,
7008                                                  gboolean batch)
7009 {
7010         Compose   *compose;
7011         GtkWidget *window;
7012         GtkWidget *vbox;
7013         GtkWidget *menubar;
7014         GtkWidget *handlebox;
7015
7016         GtkWidget *notebook;
7017         
7018         GtkWidget *attach_hbox;
7019         GtkWidget *attach_lab1;
7020         GtkWidget *attach_lab2;
7021
7022         GtkWidget *vbox2;
7023
7024         GtkWidget *label;
7025         GtkWidget *subject_hbox;
7026         GtkWidget *subject_frame;
7027         GtkWidget *subject_entry;
7028         GtkWidget *subject;
7029         GtkWidget *paned;
7030
7031         GtkWidget *edit_vbox;
7032         GtkWidget *ruler_hbox;
7033         GtkWidget *ruler;
7034         GtkWidget *scrolledwin;
7035         GtkWidget *text;
7036         GtkTextBuffer *buffer;
7037         GtkClipboard *clipboard;
7038         CLAWS_TIP_DECL();
7039
7040         UndoMain *undostruct;
7041
7042         gchar *titles[N_ATTACH_COLS];
7043         GtkWidget *popupmenu;
7044         GtkWidget *tmpl_menu;
7045         GtkActionGroup *action_group = NULL;
7046
7047 #if USE_ENCHANT
7048         GtkAspell * gtkaspell = NULL;
7049 #endif
7050
7051         static GdkGeometry geometry;
7052
7053         cm_return_val_if_fail(account != NULL, NULL);
7054
7055         debug_print("Creating compose window...\n");
7056         compose = g_new0(Compose, 1);
7057
7058         titles[COL_MIMETYPE] = _("MIME type");
7059         titles[COL_SIZE]     = _("Size");
7060         titles[COL_NAME]     = _("Name");
7061
7062         compose->batch = batch;
7063         compose->account = account;
7064         compose->folder = folder;
7065         
7066         compose->mutex = g_mutex_new();
7067         compose->set_cursor_pos = -1;
7068
7069 #if !(GTK_CHECK_VERSION(2,12,0))
7070         compose->tooltips = tips;
7071 #endif
7072
7073         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7074
7075         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7076         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7077
7078         if (!geometry.max_width) {
7079                 geometry.max_width = gdk_screen_width();
7080                 geometry.max_height = gdk_screen_height();
7081         }
7082
7083         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7084                                       &geometry, GDK_HINT_MAX_SIZE);
7085         if (!geometry.min_width) {
7086                 geometry.min_width = 600;
7087                 geometry.min_height = 440;
7088         }
7089         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7090                                       &geometry, GDK_HINT_MIN_SIZE);
7091
7092 #ifndef GENERIC_UMPC    
7093         if (compose_force_window_origin)
7094                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7095                                  prefs_common.compose_y);
7096 #endif
7097         g_signal_connect(G_OBJECT(window), "delete_event",
7098                          G_CALLBACK(compose_delete_cb), compose);
7099         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7100         gtk_widget_realize(window);
7101
7102         gtkut_widget_set_composer_icon(window);
7103
7104         vbox = gtk_vbox_new(FALSE, 0);
7105         gtk_container_add(GTK_CONTAINER(window), vbox);
7106
7107         compose->ui_manager = gtk_ui_manager_new();
7108         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7109                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7110         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7111                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7112         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7113                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7114         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7115                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7116         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7117                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7118
7119 #ifndef MAEMO
7120         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7121 #else
7122         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7123 #endif
7124
7125         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7126         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7127 #ifdef USE_ENCHANT
7128         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7129 #endif
7130         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7131         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7132         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7133
7134 /* Compose menu */
7135         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7136         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7137         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7138         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7139         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7140         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7141         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7142         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7143         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7144         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7145
7146 /* Edit menu */
7147         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7148         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7149         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7150
7151         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7152         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7153         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7154
7155         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7156         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7157         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7158         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7159
7160         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7161
7162         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7163         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7164         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7165         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7166         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7167         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7168         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7169         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7170         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7171         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7172         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7173         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7174         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7175         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7176         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7177
7178         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7179
7180         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7181         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7182         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7183         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7184         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7185
7186         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7187
7188         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7189
7190 #if USE_ENCHANT
7191 /* Spelling menu */
7192         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7193         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7194         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7195         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7196         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7197         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7198 #endif
7199
7200 /* Options menu */
7201         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7202         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7203         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7204         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7205         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7206
7207         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7208         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7209         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7210         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7211         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7212
7213         
7214         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7215         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7216         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7217         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7218         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7219         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7220         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7221
7222         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7223         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7224         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7225         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7226         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7227
7228         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7229
7230         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7231         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7232         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7233         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7234         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7235
7236         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7237         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)
7238         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)
7239         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7240
7241         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7242
7243         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7244         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)
7245         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)
7246
7247         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7248
7249         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7250         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)
7251         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7252
7253         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7254         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)
7255         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7256
7257         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7258
7259         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7260         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)
7261         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7262         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7263         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7264
7265         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7266         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)
7267         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)
7268         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7269         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7270
7271         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7272         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7273         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7274         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7275         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7276         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7277
7278         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7279         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7280         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)
7281
7282         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7283         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7284         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7285 /* phew. */
7286
7287 /* Tools menu */
7288         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7289         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7290         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7291         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7292         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7293         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7294
7295 /* Help menu */
7296         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7297
7298         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7299         gtk_widget_show_all(menubar);
7300
7301         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7302 #ifndef MAEMO
7303         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7304 #else
7305         hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7306 #endif
7307
7308         if (prefs_common.toolbar_detachable) {
7309                 handlebox = gtk_handle_box_new();
7310         } else {
7311                 handlebox = gtk_hbox_new(FALSE, 0);
7312         }
7313         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7314
7315         gtk_widget_realize(handlebox);
7316 #ifdef MAEMO
7317         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7318                                           (gpointer)compose);
7319 #else
7320         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7321                                           (gpointer)compose);
7322 #endif
7323
7324         vbox2 = gtk_vbox_new(FALSE, 2);
7325         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7326         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7327         
7328         /* Notebook */
7329         notebook = gtk_notebook_new();
7330         gtk_widget_set_size_request(notebook, -1, 130);
7331         gtk_widget_show(notebook);
7332
7333         /* header labels and entries */
7334         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7335                         compose_create_header(compose),
7336                         gtk_label_new_with_mnemonic(_("Hea_der")));
7337         /* attachment list */
7338         attach_hbox = gtk_hbox_new(FALSE, 0);
7339         gtk_widget_show(attach_hbox);
7340         
7341         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7342         gtk_widget_show(attach_lab1);
7343         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7344         
7345         attach_lab2 = gtk_label_new("");
7346         gtk_widget_show(attach_lab2);
7347         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7348         
7349         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7350                         compose_create_attach(compose),
7351                         attach_hbox);
7352         /* Others Tab */
7353         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7354                         compose_create_others(compose),
7355                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7356
7357         /* Subject */
7358         subject_hbox = gtk_hbox_new(FALSE, 0);
7359         gtk_widget_show(subject_hbox);
7360
7361         subject_frame = gtk_frame_new(NULL);
7362         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7363         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7364         gtk_widget_show(subject_frame);
7365
7366         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7367         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7368         gtk_widget_show(subject);
7369
7370         label = gtk_label_new(_("Subject:"));
7371         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7372         gtk_widget_show(label);
7373
7374 #ifdef USE_ENCHANT
7375         subject_entry = claws_spell_entry_new();
7376 #else
7377         subject_entry = gtk_entry_new();
7378 #endif
7379         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7380         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7381                          G_CALLBACK(compose_grab_focus_cb), compose);
7382         gtk_widget_show(subject_entry);
7383         compose->subject_entry = subject_entry;
7384         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7385         
7386         edit_vbox = gtk_vbox_new(FALSE, 0);
7387
7388         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7389
7390         /* ruler */
7391         ruler_hbox = gtk_hbox_new(FALSE, 0);
7392         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7393
7394         ruler = gtk_shruler_new();
7395         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7396         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7397                            BORDER_WIDTH);
7398
7399         /* text widget */
7400         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7401         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7402                                        GTK_POLICY_AUTOMATIC,
7403                                        GTK_POLICY_AUTOMATIC);
7404         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7405                                             GTK_SHADOW_IN);
7406         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7407         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7408
7409         text = gtk_text_view_new();
7410         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7411         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7412         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7413         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7414         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7415         
7416         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7417
7418         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7419                                G_CALLBACK(compose_edit_size_alloc),
7420                                ruler);
7421         g_signal_connect(G_OBJECT(buffer), "changed",
7422                          G_CALLBACK(compose_changed_cb), compose);
7423         g_signal_connect(G_OBJECT(text), "grab_focus",
7424                          G_CALLBACK(compose_grab_focus_cb), compose);
7425         g_signal_connect(G_OBJECT(buffer), "insert_text",
7426                          G_CALLBACK(text_inserted), compose);
7427         g_signal_connect(G_OBJECT(text), "button_press_event",
7428                          G_CALLBACK(text_clicked), compose);
7429 #ifndef MAEMO
7430         g_signal_connect(G_OBJECT(text), "popup-menu",
7431                          G_CALLBACK(compose_popup_menu), compose);
7432 #else
7433         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7434                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7435         g_signal_connect(G_OBJECT(text), "tap-and-hold",
7436                          G_CALLBACK(compose_popup_menu), compose);
7437 #endif
7438         g_signal_connect(G_OBJECT(subject_entry), "changed",
7439                          G_CALLBACK(compose_changed_cb), compose);
7440
7441         /* drag and drop */
7442         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7443                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7444                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7445         g_signal_connect(G_OBJECT(text), "drag_data_received",
7446                          G_CALLBACK(compose_insert_drag_received_cb),
7447                          compose);
7448         g_signal_connect(G_OBJECT(text), "drag-drop",
7449                          G_CALLBACK(compose_drag_drop),
7450                          compose);
7451         gtk_widget_show_all(vbox);
7452
7453         /* pane between attach clist and text */
7454         paned = gtk_vpaned_new();
7455         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7456 #ifdef MAEMO
7457         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7458                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7459         else
7460                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7461 #endif
7462         gtk_paned_add1(GTK_PANED(paned), notebook);
7463         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7464         gtk_widget_show_all(paned);
7465
7466
7467         if (prefs_common.textfont) {
7468                 PangoFontDescription *font_desc;
7469
7470                 font_desc = pango_font_description_from_string
7471                         (prefs_common.textfont);
7472                 if (font_desc) {
7473                         gtk_widget_modify_font(text, font_desc);
7474                         pango_font_description_free(font_desc);
7475                 }
7476         }
7477
7478         gtk_action_group_add_actions(action_group, compose_popup_entries,
7479                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7480         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7481         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7482         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7483         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7484         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7485         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7486         
7487         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7488
7489         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7490         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7491         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7492
7493         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7494
7495         undostruct = undo_init(text);
7496         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7497                                    compose);
7498
7499         address_completion_start(window);
7500
7501         compose->window        = window;
7502         compose->vbox          = vbox;
7503         compose->menubar       = menubar;
7504         compose->handlebox     = handlebox;
7505
7506         compose->vbox2         = vbox2;
7507
7508         compose->paned = paned;
7509
7510         compose->attach_label  = attach_lab2;
7511
7512         compose->notebook      = notebook;
7513         compose->edit_vbox     = edit_vbox;
7514         compose->ruler_hbox    = ruler_hbox;
7515         compose->ruler         = ruler;
7516         compose->scrolledwin   = scrolledwin;
7517         compose->text          = text;
7518
7519         compose->focused_editable = NULL;
7520
7521         compose->popupmenu    = popupmenu;
7522
7523         compose->tmpl_menu = tmpl_menu;
7524
7525         compose->mode = mode;
7526         compose->rmode = mode;
7527
7528         compose->targetinfo = NULL;
7529         compose->replyinfo  = NULL;
7530         compose->fwdinfo    = NULL;
7531
7532         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7533                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7534         
7535         compose->replyto     = NULL;
7536         compose->cc          = NULL;
7537         compose->bcc         = NULL;
7538         compose->followup_to = NULL;
7539
7540         compose->ml_post     = NULL;
7541
7542         compose->inreplyto   = NULL;
7543         compose->references  = NULL;
7544         compose->msgid       = NULL;
7545         compose->boundary    = NULL;
7546
7547         compose->autowrap       = prefs_common.autowrap;
7548         compose->autoindent     = prefs_common.auto_indent;
7549         compose->use_signing    = FALSE;
7550         compose->use_encryption = FALSE;
7551         compose->privacy_system = NULL;
7552
7553         compose->modified = FALSE;
7554
7555         compose->return_receipt = FALSE;
7556
7557         compose->to_list        = NULL;
7558         compose->newsgroup_list = NULL;
7559
7560         compose->undostruct = undostruct;
7561
7562         compose->sig_str = NULL;
7563
7564         compose->exteditor_file    = NULL;
7565         compose->exteditor_pid     = -1;
7566         compose->exteditor_tag     = -1;
7567         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7568
7569 #if USE_ENCHANT
7570         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7571         if (mode != COMPOSE_REDIRECT) {
7572                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7573                     strcmp(prefs_common.dictionary, "")) {
7574                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7575                                                   prefs_common.alt_dictionary,
7576                                                   conv_get_locale_charset_str(),
7577                                                   prefs_common.misspelled_col,
7578                                                   prefs_common.check_while_typing,
7579                                                   prefs_common.recheck_when_changing_dict,
7580                                                   prefs_common.use_alternate,
7581                                                   prefs_common.use_both_dicts,
7582                                                   GTK_TEXT_VIEW(text),
7583                                                   GTK_WINDOW(compose->window),
7584                                                   compose_dict_changed,
7585                                                   compose_spell_menu_changed,
7586                                                   compose);
7587                         if (!gtkaspell) {
7588                                 alertpanel_error(_("Spell checker could not "
7589                                                 "be started.\n%s"),
7590                                                 gtkaspell_checkers_strerror());
7591                                 gtkaspell_checkers_reset_error();
7592                         } else {
7593                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7594                         }
7595                 }
7596         }
7597     compose->gtkaspell = gtkaspell;
7598         compose_spell_menu_changed(compose);
7599         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7600 #endif
7601
7602         compose_select_account(compose, account, TRUE);
7603
7604         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7605         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7606
7607         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7608                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7609
7610         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7611                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7612         
7613         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7614                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7615
7616         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7617         if (account->protocol != A_NNTP)
7618                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7619                                 prefs_common_translated_header_name("To:"));
7620         else
7621                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7622                                 prefs_common_translated_header_name("Newsgroups:"));
7623
7624         addressbook_set_target_compose(compose);
7625         
7626         if (mode != COMPOSE_REDIRECT)
7627                 compose_set_template_menu(compose);
7628         else {
7629                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7630         }
7631
7632         compose_list = g_list_append(compose_list, compose);
7633
7634         if (!prefs_common.show_ruler)
7635                 gtk_widget_hide(ruler_hbox);
7636                 
7637         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7638
7639         /* Priority */
7640         compose->priority = PRIORITY_NORMAL;
7641         compose_update_priority_menu_item(compose);
7642
7643         compose_set_out_encoding(compose);
7644         
7645         /* Actions menu */
7646         compose_update_actions_menu(compose);
7647
7648         /* Privacy Systems menu */
7649         compose_update_privacy_systems_menu(compose);
7650
7651         activate_privacy_system(compose, account, TRUE);
7652         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7653         if (batch) {
7654                 gtk_widget_realize(window);
7655         } else {
7656                 gtk_widget_show(window);
7657 #ifdef MAEMO
7658                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7659                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7660 #endif
7661         }
7662         
7663         return compose;
7664 }
7665
7666 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7667 {
7668         GList *accounts;
7669         GtkWidget *hbox;
7670         GtkWidget *optmenu;
7671         GtkWidget *optmenubox;
7672         GtkListStore *menu;
7673         GtkTreeIter iter;
7674         GtkWidget *from_name = NULL;
7675 #if !(GTK_CHECK_VERSION(2,12,0))
7676         GtkTooltips *tips = compose->tooltips;
7677 #endif
7678
7679         gint num = 0, def_menu = 0;
7680         
7681         accounts = account_get_list();
7682         cm_return_val_if_fail(accounts != NULL, NULL);
7683
7684         optmenubox = gtk_event_box_new();
7685         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7686         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7687
7688         hbox = gtk_hbox_new(FALSE, 6);
7689         from_name = gtk_entry_new();
7690         
7691         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7692                          G_CALLBACK(compose_grab_focus_cb), compose);
7693
7694         for (; accounts != NULL; accounts = accounts->next, num++) {
7695                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7696                 gchar *name, *from = NULL;
7697
7698                 if (ac == compose->account) def_menu = num;
7699
7700                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7701                                        ac->account_name);
7702                 
7703                 if (ac == compose->account) {
7704                         if (ac->name && *ac->name) {
7705                                 gchar *buf;
7706                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7707                                 from = g_strdup_printf("%s <%s>",
7708                                                        buf, ac->address);
7709                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7710                         } else {
7711                                 from = g_strdup_printf("%s",
7712                                                        ac->address);
7713                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7714                         }
7715                 }
7716                 COMBOBOX_ADD(menu, name, ac->account_id);
7717                 g_free(name);
7718                 g_free(from);
7719         }
7720
7721         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7722
7723         g_signal_connect(G_OBJECT(optmenu), "changed",
7724                         G_CALLBACK(account_activated),
7725                         compose);
7726         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7727                          G_CALLBACK(compose_entry_popup_extend),
7728                          NULL);
7729
7730         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7731         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7732         
7733         CLAWS_SET_TIP(optmenubox,
7734                 _("Account to use for this email"));
7735         CLAWS_SET_TIP(from_name,
7736                 _("Sender address to be used"));
7737
7738         compose->account_combo = optmenu;
7739         compose->from_name = from_name;
7740         
7741         return hbox;
7742 }
7743
7744 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7745 {
7746         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7747         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7748         Compose *compose = (Compose *) data;
7749         if (active) {
7750                 compose->priority = value;
7751         }
7752 }
7753
7754 static void compose_reply_change_mode(Compose *compose,
7755                                     ComposeMode action)
7756 {
7757         gboolean was_modified = compose->modified;
7758
7759         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7760         
7761         cm_return_if_fail(compose->replyinfo != NULL);
7762         
7763         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7764                 ml = TRUE;
7765         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7766                 followup = TRUE;
7767         if (action == COMPOSE_REPLY_TO_ALL)
7768                 all = TRUE;
7769         if (action == COMPOSE_REPLY_TO_SENDER)
7770                 sender = TRUE;
7771         if (action == COMPOSE_REPLY_TO_LIST)
7772                 ml = TRUE;
7773
7774         compose_remove_header_entries(compose);
7775         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7776         if (compose->account->set_autocc && compose->account->auto_cc)
7777                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7778
7779         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7780                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7781         
7782         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7783                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7784         compose_show_first_last_header(compose, TRUE);
7785         compose->modified = was_modified;
7786         compose_set_title(compose);
7787 }
7788
7789 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7790 {
7791         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7792         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7793         Compose *compose = (Compose *) data;
7794         
7795         if (active)
7796                 compose_reply_change_mode(compose, value);
7797 }
7798
7799 static void compose_update_priority_menu_item(Compose * compose)
7800 {
7801         GtkWidget *menuitem = NULL;
7802         switch (compose->priority) {
7803                 case PRIORITY_HIGHEST:
7804                         menuitem = gtk_ui_manager_get_widget
7805                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7806                         break;
7807                 case PRIORITY_HIGH:
7808                         menuitem = gtk_ui_manager_get_widget
7809                                 (compose->ui_manager, "/Menu/Options/Priority/High");
7810                         break;
7811                 case PRIORITY_NORMAL:
7812                         menuitem = gtk_ui_manager_get_widget
7813                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7814                         break;
7815                 case PRIORITY_LOW:
7816                         menuitem = gtk_ui_manager_get_widget
7817                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
7818                         break;
7819                 case PRIORITY_LOWEST:
7820                         menuitem = gtk_ui_manager_get_widget
7821                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7822                         break;
7823         }
7824         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7825 }       
7826
7827 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7828 {
7829         Compose *compose = (Compose *) data;
7830         gchar *systemid;
7831         gboolean can_sign = FALSE, can_encrypt = FALSE;
7832
7833         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7834
7835         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7836                 return;
7837
7838         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7839         g_free(compose->privacy_system);
7840         compose->privacy_system = NULL;
7841         if (systemid != NULL) {
7842                 compose->privacy_system = g_strdup(systemid);
7843
7844                 can_sign = privacy_system_can_sign(systemid);
7845                 can_encrypt = privacy_system_can_encrypt(systemid);
7846         }
7847
7848         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7849
7850         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7851         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7852 }
7853
7854 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7855 {
7856         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7857         GtkWidget *menuitem = NULL;
7858         GList *amenu;
7859         gboolean can_sign = FALSE, can_encrypt = FALSE;
7860         gboolean found = FALSE;
7861
7862         if (compose->privacy_system != NULL) {
7863                 gchar *systemid;
7864                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7865                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7866                 cm_return_if_fail(menuitem != NULL);
7867
7868                 amenu = GTK_MENU_SHELL(menuitem)->children;
7869                 menuitem = NULL;
7870                 while (amenu != NULL) {
7871                         GList *alist = amenu->next;
7872
7873                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7874                         if (systemid != NULL) {
7875                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
7876                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7877                                         menuitem = GTK_WIDGET(amenu->data);
7878
7879                                         can_sign = privacy_system_can_sign(systemid);
7880                                         can_encrypt = privacy_system_can_encrypt(systemid);
7881                                         found = TRUE;
7882                                         break;
7883                                 } 
7884                         } else if (strlen(compose->privacy_system) == 0 && 
7885                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7886                                         menuitem = GTK_WIDGET(amenu->data);
7887
7888                                         can_sign = FALSE;
7889                                         can_encrypt = FALSE;
7890                                         found = TRUE;
7891                                         break;
7892                         }
7893
7894                         amenu = alist;
7895                 }
7896                 if (menuitem != NULL)
7897                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7898                 
7899                 if (warn && !found && strlen(compose->privacy_system)) {
7900                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7901                                   "will not be able to sign or encrypt this message."),
7902                                   compose->privacy_system);
7903                 }
7904         } 
7905
7906         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7907         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7908 }       
7909  
7910 static void compose_set_out_encoding(Compose *compose)
7911 {
7912         CharSet out_encoding;
7913         const gchar *branch = NULL;
7914         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7915
7916         switch(out_encoding) {
7917                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7918                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7919                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7920                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7921                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7922                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7923                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7924                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7925                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7926                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7927                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7928                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7929                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7930                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7931                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7932                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7933                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7934                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7935                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7936                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7937                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7938                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7939                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7940                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7941                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7942                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7943                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7944                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7945                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7946                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7947                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7948                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7949                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7950         }
7951         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7952 }
7953
7954 static void compose_set_template_menu(Compose *compose)
7955 {
7956         GSList *tmpl_list, *cur;
7957         GtkWidget *menu;
7958         GtkWidget *item;
7959
7960         tmpl_list = template_get_config();
7961
7962         menu = gtk_menu_new();
7963
7964         gtk_menu_set_accel_group (GTK_MENU (menu), 
7965                 gtk_ui_manager_get_accel_group(compose->ui_manager));
7966         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7967                 Template *tmpl = (Template *)cur->data;
7968                 gchar *accel_path = NULL;
7969                 item = gtk_menu_item_new_with_label(tmpl->name);
7970                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7971                 g_signal_connect(G_OBJECT(item), "activate",
7972                                  G_CALLBACK(compose_template_activate_cb),
7973                                  compose);
7974                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7975                 gtk_widget_show(item);
7976                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7977                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7978                 g_free(accel_path);
7979         }
7980
7981         gtk_widget_show(menu);
7982         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7983 }
7984
7985 void compose_update_actions_menu(Compose *compose)
7986 {
7987         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7988 }
7989
7990 static void compose_update_privacy_systems_menu(Compose *compose)
7991 {
7992         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7993         GSList *systems, *cur;
7994         GtkWidget *widget;
7995         GtkWidget *system_none;
7996         GSList *group;
7997         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7998         GtkWidget *privacy_menu = gtk_menu_new();
7999
8000         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8001         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8002
8003         g_signal_connect(G_OBJECT(system_none), "activate",
8004                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8005
8006         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8007         gtk_widget_show(system_none);
8008
8009         systems = privacy_get_system_ids();
8010         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8011                 gchar *systemid = cur->data;
8012
8013                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8014                 widget = gtk_radio_menu_item_new_with_label(group,
8015                         privacy_system_get_name(systemid));
8016                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8017                                        g_strdup(systemid), g_free);
8018                 g_signal_connect(G_OBJECT(widget), "activate",
8019                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8020
8021                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8022                 gtk_widget_show(widget);
8023                 g_free(systemid);
8024         }
8025         g_slist_free(systems);
8026         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8027         gtk_widget_show_all(privacy_menu);
8028         gtk_widget_show_all(privacy_menuitem);
8029 }
8030
8031 void compose_reflect_prefs_all(void)
8032 {
8033         GList *cur;
8034         Compose *compose;
8035
8036         for (cur = compose_list; cur != NULL; cur = cur->next) {
8037                 compose = (Compose *)cur->data;
8038                 compose_set_template_menu(compose);
8039         }
8040 }
8041
8042 void compose_reflect_prefs_pixmap_theme(void)
8043 {
8044         GList *cur;
8045         Compose *compose;
8046
8047         for (cur = compose_list; cur != NULL; cur = cur->next) {
8048                 compose = (Compose *)cur->data;
8049                 toolbar_update(TOOLBAR_COMPOSE, compose);
8050         }
8051 }
8052
8053 static const gchar *compose_quote_char_from_context(Compose *compose)
8054 {
8055         const gchar *qmark = NULL;
8056
8057         cm_return_val_if_fail(compose != NULL, NULL);
8058
8059         switch (compose->mode) {
8060                 /* use forward-specific quote char */
8061                 case COMPOSE_FORWARD:
8062                 case COMPOSE_FORWARD_AS_ATTACH:
8063                 case COMPOSE_FORWARD_INLINE:
8064                         if (compose->folder && compose->folder->prefs &&
8065                                         compose->folder->prefs->forward_with_format)
8066                                 qmark = compose->folder->prefs->forward_quotemark;
8067                         else if (compose->account->forward_with_format)
8068                                 qmark = compose->account->forward_quotemark;
8069                         else
8070                                 qmark = prefs_common.fw_quotemark;
8071                         break;
8072
8073                 /* use reply-specific quote char in all other modes */
8074                 default:
8075                         if (compose->folder && compose->folder->prefs &&
8076                                         compose->folder->prefs->reply_with_format)
8077                                 qmark = compose->folder->prefs->reply_quotemark;
8078                         else if (compose->account->reply_with_format)
8079                                 qmark = compose->account->reply_quotemark;
8080                         else
8081                                 qmark = prefs_common.quotemark;
8082                         break;
8083         }
8084
8085         if (qmark == NULL || *qmark == '\0')
8086                 qmark = "> ";
8087
8088         return qmark;
8089 }
8090
8091 static void compose_template_apply(Compose *compose, Template *tmpl,
8092                                    gboolean replace)
8093 {
8094         GtkTextView *text;
8095         GtkTextBuffer *buffer;
8096         GtkTextMark *mark;
8097         GtkTextIter iter;
8098         const gchar *qmark;
8099         gchar *parsed_str = NULL;
8100         gint cursor_pos = 0;
8101         const gchar *err_msg = _("The body of the template has an error at line %d.");
8102         if (!tmpl) return;
8103
8104         /* process the body */
8105
8106         text = GTK_TEXT_VIEW(compose->text);
8107         buffer = gtk_text_view_get_buffer(text);
8108
8109         if (tmpl->value) {
8110                 qmark = compose_quote_char_from_context(compose);
8111
8112                 if (compose->replyinfo != NULL) {
8113
8114                         if (replace)
8115                                 gtk_text_buffer_set_text(buffer, "", -1);
8116                         mark = gtk_text_buffer_get_insert(buffer);
8117                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8118
8119                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8120                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8121
8122                 } else if (compose->fwdinfo != NULL) {
8123
8124                         if (replace)
8125                                 gtk_text_buffer_set_text(buffer, "", -1);
8126                         mark = gtk_text_buffer_get_insert(buffer);
8127                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8128
8129                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8130                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8131
8132                 } else {
8133                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8134
8135                         GtkTextIter start, end;
8136                         gchar *tmp = NULL;
8137
8138                         gtk_text_buffer_get_start_iter(buffer, &start);
8139                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8140                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8141
8142                         /* clear the buffer now */
8143                         if (replace)
8144                                 gtk_text_buffer_set_text(buffer, "", -1);
8145
8146                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8147                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8148                         procmsg_msginfo_free( dummyinfo );
8149
8150                         g_free( tmp );
8151                 } 
8152         } else {
8153                 if (replace)
8154                         gtk_text_buffer_set_text(buffer, "", -1);
8155                 mark = gtk_text_buffer_get_insert(buffer);
8156                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8157         }       
8158
8159         if (replace && parsed_str && compose->account->auto_sig)
8160                 compose_insert_sig(compose, FALSE);
8161
8162         if (replace && parsed_str) {
8163                 gtk_text_buffer_get_start_iter(buffer, &iter);
8164                 gtk_text_buffer_place_cursor(buffer, &iter);
8165         }
8166         
8167         if (parsed_str) {
8168                 cursor_pos = quote_fmt_get_cursor_pos();
8169                 compose->set_cursor_pos = cursor_pos;
8170                 if (cursor_pos == -1)
8171                         cursor_pos = 0;
8172                 gtk_text_buffer_get_start_iter(buffer, &iter);
8173                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8174                 gtk_text_buffer_place_cursor(buffer, &iter);
8175         }
8176
8177         /* process the other fields */
8178
8179         compose_template_apply_fields(compose, tmpl);
8180         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8181         quote_fmt_reset_vartable();
8182         compose_changed_cb(NULL, compose);
8183
8184 #ifdef USE_ENCHANT
8185         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8186                 gtkaspell_highlight_all(compose->gtkaspell);
8187 #endif
8188 }
8189
8190 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8191 {
8192         MsgInfo* dummyinfo = NULL;
8193         MsgInfo *msginfo = NULL;
8194         gchar *buf = NULL;
8195
8196         if (compose->replyinfo != NULL)
8197                 msginfo = compose->replyinfo;
8198         else if (compose->fwdinfo != NULL)
8199                 msginfo = compose->fwdinfo;
8200         else {
8201                 dummyinfo = compose_msginfo_new_from_compose(compose);
8202                 msginfo = dummyinfo;
8203         }
8204
8205         if (tmpl->from && *tmpl->from != '\0') {
8206 #ifdef USE_ENCHANT
8207                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8208                                 compose->gtkaspell);
8209 #else
8210                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8211 #endif
8212                 quote_fmt_scan_string(tmpl->from);
8213                 quote_fmt_parse();
8214
8215                 buf = quote_fmt_get_buffer();
8216                 if (buf == NULL) {
8217                         alertpanel_error(_("Template From format error."));
8218                 } else {
8219                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8220                 }
8221         }
8222
8223         if (tmpl->to && *tmpl->to != '\0') {
8224 #ifdef USE_ENCHANT
8225                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8226                                 compose->gtkaspell);
8227 #else
8228                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8229 #endif
8230                 quote_fmt_scan_string(tmpl->to);
8231                 quote_fmt_parse();
8232
8233                 buf = quote_fmt_get_buffer();
8234                 if (buf == NULL) {
8235                         alertpanel_error(_("Template To format error."));
8236                 } else {
8237                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8238                 }
8239         }
8240
8241         if (tmpl->cc && *tmpl->cc != '\0') {
8242 #ifdef USE_ENCHANT
8243                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8244                                 compose->gtkaspell);
8245 #else
8246                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8247 #endif
8248                 quote_fmt_scan_string(tmpl->cc);
8249                 quote_fmt_parse();
8250
8251                 buf = quote_fmt_get_buffer();
8252                 if (buf == NULL) {
8253                         alertpanel_error(_("Template Cc format error."));
8254                 } else {
8255                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8256                 }
8257         }
8258
8259         if (tmpl->bcc && *tmpl->bcc != '\0') {
8260 #ifdef USE_ENCHANT
8261                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8262                                 compose->gtkaspell);
8263 #else
8264                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8265 #endif
8266                 quote_fmt_scan_string(tmpl->bcc);
8267                 quote_fmt_parse();
8268
8269                 buf = quote_fmt_get_buffer();
8270                 if (buf == NULL) {
8271                         alertpanel_error(_("Template Bcc format error."));
8272                 } else {
8273                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8274                 }
8275         }
8276
8277         /* process the subject */
8278         if (tmpl->subject && *tmpl->subject != '\0') {
8279 #ifdef USE_ENCHANT
8280                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8281                                 compose->gtkaspell);
8282 #else
8283                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8284 #endif
8285                 quote_fmt_scan_string(tmpl->subject);
8286                 quote_fmt_parse();
8287
8288                 buf = quote_fmt_get_buffer();
8289                 if (buf == NULL) {
8290                         alertpanel_error(_("Template subject format error."));
8291                 } else {
8292                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8293                 }
8294         }
8295
8296         procmsg_msginfo_free( dummyinfo );
8297 }
8298
8299 static void compose_destroy(Compose *compose)
8300 {
8301         GtkTextBuffer *buffer;
8302         GtkClipboard *clipboard;
8303
8304         compose_list = g_list_remove(compose_list, compose);
8305
8306         if (compose->updating) {
8307                 debug_print("danger, not destroying anything now\n");
8308                 compose->deferred_destroy = TRUE;
8309                 return;
8310         }
8311         /* NOTE: address_completion_end() does nothing with the window
8312          * however this may change. */
8313         address_completion_end(compose->window);
8314
8315         slist_free_strings(compose->to_list);
8316         g_slist_free(compose->to_list);
8317         slist_free_strings(compose->newsgroup_list);
8318         g_slist_free(compose->newsgroup_list);
8319         slist_free_strings(compose->header_list);
8320         g_slist_free(compose->header_list);
8321
8322         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8323
8324         g_hash_table_destroy(compose->email_hashtable);
8325
8326         procmsg_msginfo_free(compose->targetinfo);
8327         procmsg_msginfo_free(compose->replyinfo);
8328         procmsg_msginfo_free(compose->fwdinfo);
8329
8330         g_free(compose->replyto);
8331         g_free(compose->cc);
8332         g_free(compose->bcc);
8333         g_free(compose->newsgroups);
8334         g_free(compose->followup_to);
8335
8336         g_free(compose->ml_post);
8337
8338         g_free(compose->inreplyto);
8339         g_free(compose->references);
8340         g_free(compose->msgid);
8341         g_free(compose->boundary);
8342
8343         g_free(compose->redirect_filename);
8344         if (compose->undostruct)
8345                 undo_destroy(compose->undostruct);
8346
8347         g_free(compose->sig_str);
8348
8349         g_free(compose->exteditor_file);
8350
8351         g_free(compose->orig_charset);
8352
8353         g_free(compose->privacy_system);
8354
8355         if (addressbook_get_target_compose() == compose)
8356                 addressbook_set_target_compose(NULL);
8357
8358 #if USE_ENCHANT
8359         if (compose->gtkaspell) {
8360                 gtkaspell_delete(compose->gtkaspell);
8361                 compose->gtkaspell = NULL;
8362         }
8363 #endif
8364
8365         if (!compose->batch) {
8366                 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8367                 prefs_common.compose_height = compose->window->allocation.height;
8368         }
8369
8370         if (!gtk_widget_get_parent(compose->paned))
8371                 gtk_widget_destroy(compose->paned);
8372         gtk_widget_destroy(compose->popupmenu);
8373
8374         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8375         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8376         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8377
8378         gtk_widget_destroy(compose->window);
8379         toolbar_destroy(compose->toolbar);
8380         g_free(compose->toolbar);
8381         g_mutex_free(compose->mutex);
8382         g_free(compose);
8383 }
8384
8385 static void compose_attach_info_free(AttachInfo *ainfo)
8386 {
8387         g_free(ainfo->file);
8388         g_free(ainfo->content_type);
8389         g_free(ainfo->name);
8390         g_free(ainfo);
8391 }
8392
8393 static void compose_attach_update_label(Compose *compose)
8394 {
8395         GtkTreeIter iter;
8396         gint i = 1;
8397         gchar *text;
8398         GtkTreeModel *model;
8399         
8400         if(compose == NULL)
8401                 return;
8402                 
8403         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8404         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8405                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8406                 return;
8407         }
8408         
8409         while(gtk_tree_model_iter_next(model, &iter))
8410                 i++;
8411         
8412         text = g_strdup_printf("(%d)", i);
8413         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8414         g_free(text);
8415 }
8416
8417 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8418 {
8419         Compose *compose = (Compose *)data;
8420         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8421         GtkTreeSelection *selection;
8422         GList *sel, *cur;
8423         GtkTreeModel *model;
8424
8425         selection = gtk_tree_view_get_selection(tree_view);
8426         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8427
8428         if (!sel) 
8429                 return;
8430
8431         for (cur = sel; cur != NULL; cur = cur->next) {
8432                 GtkTreePath *path = cur->data;
8433                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8434                                                 (model, cur->data);
8435                 cur->data = ref;
8436                 gtk_tree_path_free(path);
8437         }
8438
8439         for (cur = sel; cur != NULL; cur = cur->next) {
8440                 GtkTreeRowReference *ref = cur->data;
8441                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8442                 GtkTreeIter iter;
8443
8444                 if (gtk_tree_model_get_iter(model, &iter, path))
8445                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8446                 
8447                 gtk_tree_path_free(path);
8448                 gtk_tree_row_reference_free(ref);
8449         }
8450
8451         g_list_free(sel);
8452         compose_attach_update_label(compose);
8453 }
8454
8455 static struct _AttachProperty
8456 {
8457         GtkWidget *window;
8458         GtkWidget *mimetype_entry;
8459         GtkWidget *encoding_optmenu;
8460         GtkWidget *path_entry;
8461         GtkWidget *filename_entry;
8462         GtkWidget *ok_btn;
8463         GtkWidget *cancel_btn;
8464 } attach_prop;
8465
8466 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8467 {       
8468         gtk_tree_path_free((GtkTreePath *)ptr);
8469 }
8470
8471 static void compose_attach_property(GtkAction *action, gpointer data)
8472 {
8473         Compose *compose = (Compose *)data;
8474         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8475         AttachInfo *ainfo;
8476         GtkComboBox *optmenu;
8477         GtkTreeSelection *selection;
8478         GList *sel;
8479         GtkTreeModel *model;
8480         GtkTreeIter iter;
8481         GtkTreePath *path;
8482         static gboolean cancelled;
8483
8484         /* only if one selected */
8485         selection = gtk_tree_view_get_selection(tree_view);
8486         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8487                 return;
8488
8489         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8490         if (!sel)
8491                 return;
8492
8493         path = (GtkTreePath *) sel->data;
8494         gtk_tree_model_get_iter(model, &iter, path);
8495         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8496         
8497         if (!ainfo) {
8498                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8499                 g_list_free(sel);
8500                 return;
8501         }               
8502         g_list_free(sel);
8503
8504         if (!attach_prop.window)
8505                 compose_attach_property_create(&cancelled);
8506         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8507         gtk_widget_grab_focus(attach_prop.ok_btn);
8508         gtk_widget_show(attach_prop.window);
8509         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8510
8511         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8512         if (ainfo->encoding == ENC_UNKNOWN)
8513                 combobox_select_by_data(optmenu, ENC_BASE64);
8514         else
8515                 combobox_select_by_data(optmenu, ainfo->encoding);
8516
8517         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8518                            ainfo->content_type ? ainfo->content_type : "");
8519         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8520                            ainfo->file ? ainfo->file : "");
8521         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8522                            ainfo->name ? ainfo->name : "");
8523
8524         for (;;) {
8525                 const gchar *entry_text;
8526                 gchar *text;
8527                 gchar *cnttype = NULL;
8528                 gchar *file = NULL;
8529                 off_t size = 0;
8530
8531                 cancelled = FALSE;
8532                 gtk_main();
8533
8534                 gtk_widget_hide(attach_prop.window);
8535                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8536                 
8537                 if (cancelled) 
8538                         break;
8539
8540                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8541                 if (*entry_text != '\0') {
8542                         gchar *p;
8543
8544                         text = g_strstrip(g_strdup(entry_text));
8545                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8546                                 cnttype = g_strdup(text);
8547                                 g_free(text);
8548                         } else {
8549                                 alertpanel_error(_("Invalid MIME type."));
8550                                 g_free(text);
8551                                 continue;
8552                         }
8553                 }
8554
8555                 ainfo->encoding = combobox_get_active_data(optmenu);
8556
8557                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8558                 if (*entry_text != '\0') {
8559                         if (is_file_exist(entry_text) &&
8560                             (size = get_file_size(entry_text)) > 0)
8561                                 file = g_strdup(entry_text);
8562                         else {
8563                                 alertpanel_error
8564                                         (_("File doesn't exist or is empty."));
8565                                 g_free(cnttype);
8566                                 continue;
8567                         }
8568                 }
8569
8570                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8571                 if (*entry_text != '\0') {
8572                         g_free(ainfo->name);
8573                         ainfo->name = g_strdup(entry_text);
8574                 }
8575
8576                 if (cnttype) {
8577                         g_free(ainfo->content_type);
8578                         ainfo->content_type = cnttype;
8579                 }
8580                 if (file) {
8581                         g_free(ainfo->file);
8582                         ainfo->file = file;
8583                 }
8584                 if (size)
8585                         ainfo->size = (goffset)size;
8586
8587                 /* update tree store */
8588                 text = to_human_readable(ainfo->size);
8589                 gtk_tree_model_get_iter(model, &iter, path);
8590                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8591                                    COL_MIMETYPE, ainfo->content_type,
8592                                    COL_SIZE, text,
8593                                    COL_NAME, ainfo->name,
8594                                    -1);
8595                 
8596                 break;
8597         }
8598
8599         gtk_tree_path_free(path);
8600 }
8601
8602 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8603 { \
8604         label = gtk_label_new(str); \
8605         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8606                          GTK_FILL, 0, 0, 0); \
8607         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8608  \
8609         entry = gtk_entry_new(); \
8610         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8611                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8612 }
8613
8614 static void compose_attach_property_create(gboolean *cancelled)
8615 {
8616         GtkWidget *window;
8617         GtkWidget *vbox;
8618         GtkWidget *table;
8619         GtkWidget *label;
8620         GtkWidget *mimetype_entry;
8621         GtkWidget *hbox;
8622         GtkWidget *optmenu;
8623         GtkListStore *optmenu_menu;
8624         GtkWidget *path_entry;
8625         GtkWidget *filename_entry;
8626         GtkWidget *hbbox;
8627         GtkWidget *ok_btn;
8628         GtkWidget *cancel_btn;
8629         GList     *mime_type_list, *strlist;
8630         GtkTreeIter iter;
8631
8632         debug_print("Creating attach_property window...\n");
8633
8634         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8635         gtk_widget_set_size_request(window, 480, -1);
8636         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8637         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8638         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8639         g_signal_connect(G_OBJECT(window), "delete_event",
8640                          G_CALLBACK(attach_property_delete_event),
8641                          cancelled);
8642         g_signal_connect(G_OBJECT(window), "key_press_event",
8643                          G_CALLBACK(attach_property_key_pressed),
8644                          cancelled);
8645
8646         vbox = gtk_vbox_new(FALSE, 8);
8647         gtk_container_add(GTK_CONTAINER(window), vbox);
8648
8649         table = gtk_table_new(4, 2, FALSE);
8650         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8651         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8652         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8653
8654         label = gtk_label_new(_("MIME type")); 
8655         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8656                          GTK_FILL, 0, 0, 0); 
8657         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8658         mimetype_entry = gtk_combo_box_entry_new_text(); 
8659         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8660                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8661                          
8662         /* stuff with list */
8663         mime_type_list = procmime_get_mime_type_list();
8664         strlist = NULL;
8665         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8666                 MimeType *type = (MimeType *) mime_type_list->data;
8667                 gchar *tmp;
8668
8669                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8670
8671                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8672                         g_free(tmp);
8673                 else
8674                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8675                                         (GCompareFunc)strcmp2);
8676         }
8677
8678         for (mime_type_list = strlist; mime_type_list != NULL; 
8679                 mime_type_list = mime_type_list->next) {
8680                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8681                 g_free(mime_type_list->data);
8682         }
8683         g_list_free(strlist);
8684         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8685         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8686
8687         label = gtk_label_new(_("Encoding"));
8688         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8689                          GTK_FILL, 0, 0, 0);
8690         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8691
8692         hbox = gtk_hbox_new(FALSE, 0);
8693         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8694                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8695
8696         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8697         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8698
8699         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8700         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8701         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8702         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8703         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8704
8705         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8706
8707         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8708         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8709
8710         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8711                                       &ok_btn, GTK_STOCK_OK,
8712                                       NULL, NULL);
8713         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8714         gtk_widget_grab_default(ok_btn);
8715
8716         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8717                          G_CALLBACK(attach_property_ok),
8718                          cancelled);
8719         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8720                          G_CALLBACK(attach_property_cancel),
8721                          cancelled);
8722
8723         gtk_widget_show_all(vbox);
8724
8725         attach_prop.window           = window;
8726         attach_prop.mimetype_entry   = mimetype_entry;
8727         attach_prop.encoding_optmenu = optmenu;
8728         attach_prop.path_entry       = path_entry;
8729         attach_prop.filename_entry   = filename_entry;
8730         attach_prop.ok_btn           = ok_btn;
8731         attach_prop.cancel_btn       = cancel_btn;
8732 }
8733
8734 #undef SET_LABEL_AND_ENTRY
8735
8736 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8737 {
8738         *cancelled = FALSE;
8739         gtk_main_quit();
8740 }
8741
8742 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8743 {
8744         *cancelled = TRUE;
8745         gtk_main_quit();
8746 }
8747
8748 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8749                                          gboolean *cancelled)
8750 {
8751         *cancelled = TRUE;
8752         gtk_main_quit();
8753
8754         return TRUE;
8755 }
8756
8757 static gboolean attach_property_key_pressed(GtkWidget *widget,
8758                                             GdkEventKey *event,
8759                                             gboolean *cancelled)
8760 {
8761         if (event && event->keyval == GDK_Escape) {
8762                 *cancelled = TRUE;
8763                 gtk_main_quit();
8764         }
8765         if (event && event->keyval == GDK_Return) {
8766                 *cancelled = FALSE;
8767                 gtk_main_quit();
8768                 return TRUE;
8769         }
8770         return FALSE;
8771 }
8772
8773 static void compose_exec_ext_editor(Compose *compose)
8774 {
8775 #ifdef G_OS_UNIX
8776         gchar *tmp;
8777         pid_t pid;
8778         gint pipe_fds[2];
8779
8780         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8781                               G_DIR_SEPARATOR, compose);
8782
8783         if (pipe(pipe_fds) < 0) {
8784                 perror("pipe");
8785                 g_free(tmp);
8786                 return;
8787         }
8788
8789         if ((pid = fork()) < 0) {
8790                 perror("fork");
8791                 g_free(tmp);
8792                 return;
8793         }
8794
8795         if (pid != 0) {
8796                 /* close the write side of the pipe */
8797                 close(pipe_fds[1]);
8798
8799                 compose->exteditor_file    = g_strdup(tmp);
8800                 compose->exteditor_pid     = pid;
8801
8802                 compose_set_ext_editor_sensitive(compose, FALSE);
8803
8804 #ifndef G_OS_WIN32
8805                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8806 #else
8807                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8808 #endif
8809                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8810                                                         G_IO_IN,
8811                                                         compose_input_cb,
8812                                                         compose);
8813         } else {        /* process-monitoring process */
8814                 pid_t pid_ed;
8815
8816                 if (setpgid(0, 0))
8817                         perror("setpgid");
8818
8819                 /* close the read side of the pipe */
8820                 close(pipe_fds[0]);
8821
8822                 if (compose_write_body_to_file(compose, tmp) < 0) {
8823                         fd_write_all(pipe_fds[1], "2\n", 2);
8824                         _exit(1);
8825                 }
8826
8827                 pid_ed = compose_exec_ext_editor_real(tmp);
8828                 if (pid_ed < 0) {
8829                         fd_write_all(pipe_fds[1], "1\n", 2);
8830                         _exit(1);
8831                 }
8832
8833                 /* wait until editor is terminated */
8834                 waitpid(pid_ed, NULL, 0);
8835
8836                 fd_write_all(pipe_fds[1], "0\n", 2);
8837
8838                 close(pipe_fds[1]);
8839                 _exit(0);
8840         }
8841
8842         g_free(tmp);
8843 #endif /* G_OS_UNIX */
8844 }
8845
8846 #ifdef G_OS_UNIX
8847 static gint compose_exec_ext_editor_real(const gchar *file)
8848 {
8849         gchar buf[1024];
8850         gchar *p;
8851         gchar **cmdline;
8852         pid_t pid;
8853
8854         cm_return_val_if_fail(file != NULL, -1);
8855
8856         if ((pid = fork()) < 0) {
8857                 perror("fork");
8858                 return -1;
8859         }
8860
8861         if (pid != 0) return pid;
8862
8863         /* grandchild process */
8864
8865         if (setpgid(0, getppid()))
8866                 perror("setpgid");
8867
8868         if (prefs_common_get_ext_editor_cmd() &&
8869             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8870             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8871                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8872         } else {
8873                 if (prefs_common_get_ext_editor_cmd())
8874                         g_warning("External editor command-line is invalid: '%s'\n",
8875                                   prefs_common_get_ext_editor_cmd());
8876                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8877         }
8878
8879         cmdline = strsplit_with_quote(buf, " ", 1024);
8880         execvp(cmdline[0], cmdline);
8881
8882         perror("execvp");
8883         g_strfreev(cmdline);
8884
8885         _exit(1);
8886 }
8887
8888 static gboolean compose_ext_editor_kill(Compose *compose)
8889 {
8890         pid_t pgid = compose->exteditor_pid * -1;
8891         gint ret;
8892
8893         ret = kill(pgid, 0);
8894
8895         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8896                 AlertValue val;
8897                 gchar *msg;
8898
8899                 msg = g_strdup_printf
8900                         (_("The external editor is still working.\n"
8901                            "Force terminating the process?\n"
8902                            "process group id: %d"), -pgid);
8903                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8904                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8905                         
8906                 g_free(msg);
8907
8908                 if (val == G_ALERTALTERNATE) {
8909                         g_source_remove(compose->exteditor_tag);
8910                         g_io_channel_shutdown(compose->exteditor_ch,
8911                                               FALSE, NULL);
8912                         g_io_channel_unref(compose->exteditor_ch);
8913
8914                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8915                         waitpid(compose->exteditor_pid, NULL, 0);
8916
8917                         g_warning("Terminated process group id: %d", -pgid);
8918                         g_warning("Temporary file: %s",
8919                                   compose->exteditor_file);
8920
8921                         compose_set_ext_editor_sensitive(compose, TRUE);
8922
8923                         g_free(compose->exteditor_file);
8924                         compose->exteditor_file    = NULL;
8925                         compose->exteditor_pid     = -1;
8926                         compose->exteditor_ch      = NULL;
8927                         compose->exteditor_tag     = -1;
8928                 } else
8929                         return FALSE;
8930         }
8931
8932         return TRUE;
8933 }
8934
8935 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8936                                  gpointer data)
8937 {
8938         gchar buf[3] = "3";
8939         Compose *compose = (Compose *)data;
8940         gsize bytes_read;
8941
8942         debug_print(_("Compose: input from monitoring process\n"));
8943
8944         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8945
8946         g_io_channel_shutdown(source, FALSE, NULL);
8947         g_io_channel_unref(source);
8948
8949         waitpid(compose->exteditor_pid, NULL, 0);
8950
8951         if (buf[0] == '0') {            /* success */
8952                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8953                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8954
8955                 gtk_text_buffer_set_text(buffer, "", -1);
8956                 compose_insert_file(compose, compose->exteditor_file);
8957                 compose_changed_cb(NULL, compose);
8958                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8959
8960                 if (claws_unlink(compose->exteditor_file) < 0)
8961                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8962         } else if (buf[0] == '1') {     /* failed */
8963                 g_warning("Couldn't exec external editor\n");
8964                 if (claws_unlink(compose->exteditor_file) < 0)
8965                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8966         } else if (buf[0] == '2') {
8967                 g_warning("Couldn't write to file\n");
8968         } else if (buf[0] == '3') {
8969                 g_warning("Pipe read failed\n");
8970         }
8971
8972         compose_set_ext_editor_sensitive(compose, TRUE);
8973
8974         g_free(compose->exteditor_file);
8975         compose->exteditor_file    = NULL;
8976         compose->exteditor_pid     = -1;
8977         compose->exteditor_ch      = NULL;
8978         compose->exteditor_tag     = -1;
8979
8980         return FALSE;
8981 }
8982
8983 static void compose_set_ext_editor_sensitive(Compose *compose,
8984                                              gboolean sensitive)
8985 {
8986         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8987         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8988         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8989         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8990         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8991         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8992         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8993
8994         gtk_widget_set_sensitive(compose->text,                       sensitive);
8995         if (compose->toolbar->send_btn)
8996                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8997         if (compose->toolbar->sendl_btn)
8998                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8999         if (compose->toolbar->draft_btn)
9000                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9001         if (compose->toolbar->insert_btn)
9002                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9003         if (compose->toolbar->sig_btn)
9004                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9005         if (compose->toolbar->exteditor_btn)
9006                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9007         if (compose->toolbar->linewrap_current_btn)
9008                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9009         if (compose->toolbar->linewrap_all_btn)
9010                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9011 }
9012 #endif /* G_OS_UNIX */
9013
9014 /**
9015  * compose_undo_state_changed:
9016  *
9017  * Change the sensivity of the menuentries undo and redo
9018  **/
9019 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9020                                        gint redo_state, gpointer data)
9021 {
9022         Compose *compose = (Compose *)data;
9023
9024         switch (undo_state) {
9025         case UNDO_STATE_TRUE:
9026                 if (!undostruct->undo_state) {
9027                         undostruct->undo_state = TRUE;
9028                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9029                 }
9030                 break;
9031         case UNDO_STATE_FALSE:
9032                 if (undostruct->undo_state) {
9033                         undostruct->undo_state = FALSE;
9034                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9035                 }
9036                 break;
9037         case UNDO_STATE_UNCHANGED:
9038                 break;
9039         case UNDO_STATE_REFRESH:
9040                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9041                 break;
9042         default:
9043                 g_warning("Undo state not recognized");
9044                 break;
9045         }
9046
9047         switch (redo_state) {
9048         case UNDO_STATE_TRUE:
9049                 if (!undostruct->redo_state) {
9050                         undostruct->redo_state = TRUE;
9051                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9052                 }
9053                 break;
9054         case UNDO_STATE_FALSE:
9055                 if (undostruct->redo_state) {
9056                         undostruct->redo_state = FALSE;
9057                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9058                 }
9059                 break;
9060         case UNDO_STATE_UNCHANGED:
9061                 break;
9062         case UNDO_STATE_REFRESH:
9063                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9064                 break;
9065         default:
9066                 g_warning("Redo state not recognized");
9067                 break;
9068         }
9069 }
9070
9071 /* callback functions */
9072
9073 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9074  * includes "non-client" (windows-izm) in calculation, so this calculation
9075  * may not be accurate.
9076  */
9077 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9078                                         GtkAllocation *allocation,
9079                                         GtkSHRuler *shruler)
9080 {
9081         if (prefs_common.show_ruler) {
9082                 gint char_width = 0, char_height = 0;
9083                 gint line_width_in_chars;
9084
9085                 gtkut_get_font_size(GTK_WIDGET(widget),
9086                                     &char_width, &char_height);
9087                 line_width_in_chars =
9088                         (allocation->width - allocation->x) / char_width;
9089
9090                 /* got the maximum */
9091                 gtk_ruler_set_range(GTK_RULER(shruler),
9092                                     0.0, line_width_in_chars, 0,
9093                                     /*line_width_in_chars*/ char_width);
9094         }
9095
9096         return TRUE;
9097 }
9098
9099 typedef struct {
9100         gchar                   *header;
9101         gchar                   *entry;
9102         ComposePrefType         type;
9103         gboolean                entry_marked;
9104 } HeaderEntryState;
9105
9106 static void account_activated(GtkComboBox *optmenu, gpointer data)
9107 {
9108         Compose *compose = (Compose *)data;
9109
9110         PrefsAccount *ac;
9111         gchar *folderidentifier;
9112         gint account_id = 0;
9113         GtkTreeModel *menu;
9114         GtkTreeIter iter;
9115         GSList *list, *saved_list = NULL;
9116         HeaderEntryState *state;
9117         GtkRcStyle *style = NULL;
9118         static GdkColor yellow;
9119         static gboolean color_set = FALSE;
9120
9121         /* Get ID of active account in the combo box */
9122         menu = gtk_combo_box_get_model(optmenu);
9123         gtk_combo_box_get_active_iter(optmenu, &iter);
9124         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9125
9126         ac = account_find_from_id(account_id);
9127         cm_return_if_fail(ac != NULL);
9128
9129         if (ac != compose->account) {
9130                 compose_select_account(compose, ac, FALSE);
9131
9132                 for (list = compose->header_list; list; list = list->next) {
9133                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9134                         
9135                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9136                                 compose_destroy_headerentry(compose, hentry);
9137                                 continue;
9138                         }
9139                         
9140                         state = g_malloc0(sizeof(HeaderEntryState));
9141                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9142                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9143                         state->entry = gtk_editable_get_chars(
9144                                         GTK_EDITABLE(hentry->entry), 0, -1);
9145                         state->type = hentry->type;
9146                                 
9147                         if (!color_set) {
9148                                 gdk_color_parse("#f5f6be", &yellow);
9149                                 color_set = gdk_colormap_alloc_color(
9150                                                         gdk_colormap_get_system(),
9151                                                         &yellow, FALSE, TRUE);
9152                         }
9153                                 
9154                         style = gtk_widget_get_modifier_style(hentry->entry);
9155                         state->entry_marked = gdk_color_equal(&yellow,
9156                                                 &style->base[GTK_STATE_NORMAL]);
9157
9158                         saved_list = g_slist_append(saved_list, state);
9159                         compose_destroy_headerentry(compose, hentry);
9160                 }
9161
9162                 compose->header_last = NULL;
9163                 g_slist_free(compose->header_list);
9164                 compose->header_list = NULL;
9165                 compose->header_nextrow = 1;
9166                 compose_create_header_entry(compose);
9167                 
9168                 if (ac->set_autocc && ac->auto_cc)
9169                         compose_entry_append(compose, ac->auto_cc,
9170                                                 COMPOSE_CC, PREF_ACCOUNT);
9171
9172                 if (ac->set_autobcc && ac->auto_bcc) 
9173                         compose_entry_append(compose, ac->auto_bcc,
9174                                                 COMPOSE_BCC, PREF_ACCOUNT);
9175         
9176                 if (ac->set_autoreplyto && ac->auto_replyto)
9177                         compose_entry_append(compose, ac->auto_replyto,
9178                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9179                 
9180                 for (list = saved_list; list; list = list->next) {
9181                         state = (HeaderEntryState *) list->data;
9182                         
9183                         compose_add_header_entry(compose, state->header,
9184                                                 state->entry, state->type);
9185                         if (state->entry_marked)
9186                                 compose_entry_mark_default_to(compose, state->entry);
9187                         
9188                         g_free(state->header);  
9189                         g_free(state->entry);
9190                         g_free(state);
9191                 }
9192                 g_slist_free(saved_list);
9193                 
9194                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9195                                         (ac->protocol == A_NNTP) ? 
9196                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9197         }
9198
9199         /* Set message save folder */
9200         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9201                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9202         }
9203         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9204                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9205                            
9206         compose_set_save_to(compose, NULL);
9207         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9208                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9209                                   (compose->account, F_OUTBOX));
9210                 compose_set_save_to(compose, folderidentifier);
9211                 g_free(folderidentifier);
9212         }
9213 }
9214
9215 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9216                             GtkTreeViewColumn *column, Compose *compose)
9217 {
9218         compose_attach_property(NULL, compose);
9219 }
9220
9221 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9222                                       gpointer data)
9223 {
9224         Compose *compose = (Compose *)data;
9225         GtkTreeSelection *attach_selection;
9226         gint attach_nr_selected;
9227         
9228         if (!event) return FALSE;
9229
9230         if (event->button == 3) {
9231                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9232                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9233                         
9234                 if (attach_nr_selected > 0)
9235                 {
9236                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9237                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9238                 } else {
9239                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9240                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9241                 }
9242                         
9243                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9244                                NULL, NULL, event->button, event->time);
9245                 return TRUE;                           
9246         }
9247
9248         return FALSE;
9249 }
9250
9251 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9252                                    gpointer data)
9253 {
9254         Compose *compose = (Compose *)data;
9255
9256         if (!event) return FALSE;
9257
9258         switch (event->keyval) {
9259         case GDK_Delete:
9260                 compose_attach_remove_selected(NULL, compose);
9261                 break;
9262         }
9263         return FALSE;
9264 }
9265
9266 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9267 {
9268         toolbar_comp_set_sensitive(compose, allow);
9269         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9270         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9271 #if USE_ENCHANT
9272         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9273 #endif  
9274         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9275         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9276         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9277         
9278         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9279
9280 }
9281
9282 static void compose_send_cb(GtkAction *action, gpointer data)
9283 {
9284         Compose *compose = (Compose *)data;
9285
9286         if (prefs_common.work_offline && 
9287             !inc_offline_should_override(TRUE,
9288                 _("Claws Mail needs network access in order "
9289                   "to send this email.")))
9290                 return;
9291         
9292         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9293                 g_source_remove(compose->draft_timeout_tag);
9294                 compose->draft_timeout_tag = -1;
9295         }
9296
9297         compose_send(compose);
9298 }
9299
9300 static void compose_send_later_cb(GtkAction *action, gpointer data)
9301 {
9302         Compose *compose = (Compose *)data;
9303         gint val;
9304
9305         inc_lock();
9306         compose_allow_user_actions(compose, FALSE);
9307         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9308         compose_allow_user_actions(compose, TRUE);
9309         inc_unlock();
9310
9311         if (!val) {
9312                 compose_close(compose);
9313         } else if (val == -1) {
9314                 alertpanel_error(_("Could not queue message."));
9315         } else if (val == -2) {
9316                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9317         } else if (val == -3) {
9318                 if (privacy_peek_error())
9319                 alertpanel_error(_("Could not queue message for sending:\n\n"
9320                                    "Signature failed: %s"), privacy_get_error());
9321         } else if (val == -4) {
9322                 alertpanel_error(_("Could not queue message for sending:\n\n"
9323                                    "Charset conversion failed."));
9324         } else if (val == -5) {
9325                 alertpanel_error(_("Could not queue message for sending:\n\n"
9326                                    "Couldn't get recipient encryption key."));
9327         } else if (val == -6) {
9328                 /* silent error */
9329         }
9330         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9331 }
9332
9333 #define DRAFTED_AT_EXIT "drafted_at_exit"
9334 static void compose_register_draft(MsgInfo *info)
9335 {
9336         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9337                                       DRAFTED_AT_EXIT, NULL);
9338         FILE *fp = g_fopen(filepath, "ab");
9339         
9340         if (fp) {
9341                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9342                                 info->msgnum);
9343                 fclose(fp);
9344         }
9345                 
9346         g_free(filepath);       
9347 }
9348
9349 gboolean compose_draft (gpointer data, guint action) 
9350 {
9351         Compose *compose = (Compose *)data;
9352         FolderItem *draft;
9353         gchar *tmp;
9354         gint msgnum;
9355         MsgFlags flag = {0, 0};
9356         static gboolean lock = FALSE;
9357         MsgInfo *newmsginfo;
9358         FILE *fp;
9359         gboolean target_locked = FALSE;
9360         gboolean err = FALSE;
9361
9362         if (lock) return FALSE;
9363
9364         if (compose->sending)
9365                 return TRUE;
9366
9367         draft = account_get_special_folder(compose->account, F_DRAFT);
9368         cm_return_val_if_fail(draft != NULL, FALSE);
9369         
9370         if (!g_mutex_trylock(compose->mutex)) {
9371                 /* we don't want to lock the mutex once it's available,
9372                  * because as the only other part of compose.c locking
9373                  * it is compose_close - which means once unlocked,
9374                  * the compose struct will be freed */
9375                 debug_print("couldn't lock mutex, probably sending\n");
9376                 return FALSE;
9377         }
9378         
9379         lock = TRUE;
9380
9381         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9382                               G_DIR_SEPARATOR, compose);
9383         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9384                 FILE_OP_ERROR(tmp, "fopen");
9385                 goto warn_err;
9386         }
9387
9388         /* chmod for security */
9389         if (change_file_mode_rw(fp, tmp) < 0) {
9390                 FILE_OP_ERROR(tmp, "chmod");
9391                 g_warning("can't change file mode\n");
9392         }
9393
9394         /* Save draft infos */
9395         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9396         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9397
9398         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9399                 gchar *savefolderid;
9400
9401                 savefolderid = compose_get_save_to(compose);
9402                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9403                 g_free(savefolderid);
9404         }
9405         if (compose->return_receipt) {
9406                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9407         }
9408         if (compose->privacy_system) {
9409                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9410                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9411                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9412         }
9413
9414         /* Message-ID of message replying to */
9415         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9416                 gchar *folderid;
9417                 
9418                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9419                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9420                 g_free(folderid);
9421         }
9422         /* Message-ID of message forwarding to */
9423         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9424                 gchar *folderid;
9425                 
9426                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9427                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9428                 g_free(folderid);
9429         }
9430
9431         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9432         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9433
9434         /* end of headers */
9435         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9436
9437         if (err) {
9438                 fclose(fp);
9439                 goto warn_err;
9440         }
9441
9442         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9443                 fclose(fp);
9444                 goto warn_err;
9445         }
9446         if (fclose(fp) == EOF) {
9447                 goto warn_err;
9448         }
9449         
9450         if (compose->targetinfo) {
9451                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9452                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9453         }
9454         flag.tmp_flags = MSG_DRAFT;
9455
9456         folder_item_scan(draft);
9457         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9458                 MsgInfo *tmpinfo = NULL;
9459                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9460                 if (compose->msgid) {
9461                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9462                 }
9463                 if (tmpinfo) {
9464                         msgnum = tmpinfo->msgnum;
9465                         procmsg_msginfo_free(tmpinfo);
9466                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9467                 } else {
9468                         debug_print("didn't get draft msgnum after scanning\n");
9469                 }
9470         } else {
9471                 debug_print("got draft msgnum %d from adding\n", msgnum);
9472         }
9473         if (msgnum < 0) {
9474 warn_err:
9475                 claws_unlink(tmp);
9476                 g_free(tmp);
9477                 if (action != COMPOSE_AUTO_SAVE) {
9478                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9479                                 alertpanel_error(_("Could not save draft."));
9480                         else {
9481                                 AlertValue val;
9482                                 gtkut_window_popup(compose->window);
9483                                 val = alertpanel_full(_("Could not save draft"),
9484                                         _("Could not save draft.\n"
9485                                         "Do you want to cancel exit or discard this email?"),
9486                                           _("_Cancel exit"), _("_Discard email"), NULL,
9487                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9488                                 if (val == G_ALERTALTERNATE) {
9489                                         lock = FALSE;
9490                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9491                                         compose_close(compose);
9492                                         return TRUE;
9493                                 } else {
9494                                         lock = FALSE;
9495                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9496                                         return FALSE;
9497                                 }
9498                         }
9499                 }
9500                 goto unlock;
9501         }
9502         g_free(tmp);
9503
9504         if (compose->mode == COMPOSE_REEDIT) {
9505                 compose_remove_reedit_target(compose, TRUE);
9506         }
9507
9508         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9509
9510         if (newmsginfo) {
9511                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9512                 if (target_locked)
9513                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9514                 else
9515                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9516                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9517                         procmsg_msginfo_set_flags(newmsginfo, 0,
9518                                                   MSG_HAS_ATTACHMENT);
9519
9520                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9521                         compose_register_draft(newmsginfo);
9522                 }
9523                 procmsg_msginfo_free(newmsginfo);
9524         }
9525         
9526         folder_item_scan(draft);
9527         
9528         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9529                 lock = FALSE;
9530                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9531                 compose_close(compose);
9532                 return TRUE;
9533         } else {
9534                 struct stat s;
9535                 gchar *path;
9536
9537                 path = folder_item_fetch_msg(draft, msgnum);
9538                 if (path == NULL) {
9539                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9540                         goto unlock;
9541                 }
9542                 if (g_stat(path, &s) < 0) {
9543                         FILE_OP_ERROR(path, "stat");
9544                         g_free(path);
9545                         goto unlock;
9546                 }
9547                 g_free(path);
9548
9549                 procmsg_msginfo_free(compose->targetinfo);
9550                 compose->targetinfo = procmsg_msginfo_new();
9551                 compose->targetinfo->msgnum = msgnum;
9552                 compose->targetinfo->size = (goffset)s.st_size;
9553                 compose->targetinfo->mtime = s.st_mtime;
9554                 compose->targetinfo->folder = draft;
9555                 if (target_locked)
9556                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9557                 compose->mode = COMPOSE_REEDIT;
9558                 
9559                 if (action == COMPOSE_AUTO_SAVE) {
9560                         compose->autosaved_draft = compose->targetinfo;
9561                 }
9562                 compose->modified = FALSE;
9563                 compose_set_title(compose);
9564         }
9565 unlock:
9566         lock = FALSE;
9567         g_mutex_unlock(compose->mutex);
9568         return TRUE;
9569 }
9570
9571 void compose_clear_exit_drafts(void)
9572 {
9573         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9574                                       DRAFTED_AT_EXIT, NULL);
9575         if (is_file_exist(filepath))
9576                 claws_unlink(filepath);
9577         
9578         g_free(filepath);
9579 }
9580
9581 void compose_reopen_exit_drafts(void)
9582 {
9583         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9584                                       DRAFTED_AT_EXIT, NULL);
9585         FILE *fp = g_fopen(filepath, "rb");
9586         gchar buf[1024];
9587         
9588         if (fp) {
9589                 while (fgets(buf, sizeof(buf), fp)) {
9590                         gchar **parts = g_strsplit(buf, "\t", 2);
9591                         const gchar *folder = parts[0];
9592                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9593                         
9594                         if (folder && *folder && msgnum > -1) {
9595                                 FolderItem *item = folder_find_item_from_identifier(folder);
9596                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9597                                 if (info)
9598                                         compose_reedit(info, FALSE);
9599                         }
9600                         g_strfreev(parts);
9601                 }       
9602                 fclose(fp);
9603         }       
9604         g_free(filepath);
9605         compose_clear_exit_drafts();
9606 }
9607
9608 static void compose_save_cb(GtkAction *action, gpointer data)
9609 {
9610         Compose *compose = (Compose *)data;
9611         compose_draft(compose, COMPOSE_KEEP_EDITING);
9612         compose->rmode = COMPOSE_REEDIT;
9613 }
9614
9615 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9616 {
9617         if (compose && file_list) {
9618                 GList *tmp;
9619
9620                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9621                         gchar *file = (gchar *) tmp->data;
9622                         gchar *utf8_filename = conv_filename_to_utf8(file);
9623                         compose_attach_append(compose, file, utf8_filename, NULL);
9624                         compose_changed_cb(NULL, compose);
9625                         if (free_data) {
9626                         g_free(file);
9627                                 tmp->data = NULL;
9628                         }
9629                         g_free(utf8_filename);
9630                 }
9631         }
9632 }
9633
9634 static void compose_attach_cb(GtkAction *action, gpointer data)
9635 {
9636         Compose *compose = (Compose *)data;
9637         GList *file_list;
9638
9639         if (compose->redirect_filename != NULL)
9640                 return;
9641
9642         file_list = filesel_select_multiple_files_open(_("Select file"));
9643
9644         if (file_list) {
9645                 compose_attach_from_list(compose, file_list, TRUE);
9646                 g_list_free(file_list);
9647         }
9648 }
9649
9650 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9651 {
9652         Compose *compose = (Compose *)data;
9653         GList *file_list;
9654         gint files_inserted = 0;
9655
9656         file_list = filesel_select_multiple_files_open(_("Select file"));
9657
9658         if (file_list) {
9659                 GList *tmp;
9660
9661                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9662                         gchar *file = (gchar *) tmp->data;
9663                         gchar *filedup = g_strdup(file);
9664                         gchar *shortfile = g_path_get_basename(filedup);
9665                         ComposeInsertResult res;
9666                         /* insert the file if the file is short or if the user confirmed that
9667                            he/she wants to insert the large file */
9668                         res = compose_insert_file(compose, file);
9669                         if (res == COMPOSE_INSERT_READ_ERROR) {
9670                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9671                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9672                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9673                                                         "for the current encoding, insertion may be incorrect."),
9674                                                         shortfile);
9675                         } else if (res == COMPOSE_INSERT_SUCCESS)
9676                                 files_inserted++;
9677
9678                         g_free(shortfile);
9679                         g_free(filedup);
9680                         g_free(file);
9681                 }
9682                 g_list_free(file_list);
9683         }
9684
9685 #ifdef USE_ENCHANT      
9686         if (files_inserted > 0 && compose->gtkaspell && 
9687             compose->gtkaspell->check_while_typing)
9688                 gtkaspell_highlight_all(compose->gtkaspell);
9689 #endif
9690 }
9691
9692 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9693 {
9694         Compose *compose = (Compose *)data;
9695
9696         compose_insert_sig(compose, FALSE);
9697 }
9698
9699 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9700                               gpointer data)
9701 {
9702         gint x, y;
9703         Compose *compose = (Compose *)data;
9704
9705         gtkut_widget_get_uposition(widget, &x, &y);
9706         if (!compose->batch) {
9707                 prefs_common.compose_x = x;
9708                 prefs_common.compose_y = y;
9709         }
9710         if (compose->sending || compose->updating)
9711                 return TRUE;
9712         compose_close_cb(NULL, compose);
9713         return TRUE;
9714 }
9715
9716 void compose_close_toolbar(Compose *compose)
9717 {
9718         compose_close_cb(NULL, compose);
9719 }
9720
9721 static void compose_close_cb(GtkAction *action, gpointer data)
9722 {
9723         Compose *compose = (Compose *)data;
9724         AlertValue val;
9725
9726 #ifdef G_OS_UNIX
9727         if (compose->exteditor_tag != -1) {
9728                 if (!compose_ext_editor_kill(compose))
9729                         return;
9730         }
9731 #endif
9732
9733         if (compose->modified) {
9734                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9735                 if (!g_mutex_trylock(compose->mutex)) {
9736                         /* we don't want to lock the mutex once it's available,
9737                          * because as the only other part of compose.c locking
9738                          * it is compose_close - which means once unlocked,
9739                          * the compose struct will be freed */
9740                         debug_print("couldn't lock mutex, probably sending\n");
9741                         return;
9742                 }
9743                 if (!reedit) {
9744                         val = alertpanel(_("Discard message"),
9745                                  _("This message has been modified. Discard it?"),
9746                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9747                 } else {
9748                         val = alertpanel(_("Save changes"),
9749                                  _("This message has been modified. Save the latest changes?"),
9750                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9751                 }
9752                 g_mutex_unlock(compose->mutex);
9753                 switch (val) {
9754                 case G_ALERTDEFAULT:
9755                         if (prefs_common.autosave && !reedit)
9756                                 compose_remove_draft(compose);                  
9757                         break;
9758                 case G_ALERTALTERNATE:
9759                         compose_draft(data, COMPOSE_QUIT_EDITING);
9760                         return;
9761                 default:
9762                         return;
9763                 }
9764         }
9765
9766         compose_close(compose);
9767 }
9768
9769 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9770 {
9771         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9772         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9773         Compose *compose = (Compose *) data;
9774
9775         if (active)
9776                 compose->out_encoding = (CharSet)value;
9777 }
9778
9779 static void compose_address_cb(GtkAction *action, gpointer data)
9780 {
9781         Compose *compose = (Compose *)data;
9782
9783         addressbook_open(compose);
9784 }
9785
9786 static void about_show_cb(GtkAction *action, gpointer data)
9787 {
9788         about_show();
9789 }
9790
9791 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9792 {
9793         Compose *compose = (Compose *)data;
9794         Template *tmpl;
9795         gchar *msg;
9796         AlertValue val;
9797
9798         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9799         cm_return_if_fail(tmpl != NULL);
9800
9801         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9802                               tmpl->name);
9803         val = alertpanel(_("Apply template"), msg,
9804                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9805         g_free(msg);
9806
9807         if (val == G_ALERTDEFAULT)
9808                 compose_template_apply(compose, tmpl, TRUE);
9809         else if (val == G_ALERTALTERNATE)
9810                 compose_template_apply(compose, tmpl, FALSE);
9811 }
9812
9813 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9814 {
9815         Compose *compose = (Compose *)data;
9816
9817         compose_exec_ext_editor(compose);
9818 }
9819
9820 static void compose_undo_cb(GtkAction *action, gpointer data)
9821 {
9822         Compose *compose = (Compose *)data;
9823         gboolean prev_autowrap = compose->autowrap;
9824
9825         compose->autowrap = FALSE;
9826         undo_undo(compose->undostruct);
9827         compose->autowrap = prev_autowrap;
9828 }
9829
9830 static void compose_redo_cb(GtkAction *action, gpointer data)
9831 {
9832         Compose *compose = (Compose *)data;
9833         gboolean prev_autowrap = compose->autowrap;
9834         
9835         compose->autowrap = FALSE;
9836         undo_redo(compose->undostruct);
9837         compose->autowrap = prev_autowrap;
9838 }
9839
9840 static void entry_cut_clipboard(GtkWidget *entry)
9841 {
9842         if (GTK_IS_EDITABLE(entry))
9843                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9844         else if (GTK_IS_TEXT_VIEW(entry))
9845                 gtk_text_buffer_cut_clipboard(
9846                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9847                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9848                         TRUE);
9849 }
9850
9851 static void entry_copy_clipboard(GtkWidget *entry)
9852 {
9853         if (GTK_IS_EDITABLE(entry))
9854                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9855         else if (GTK_IS_TEXT_VIEW(entry))
9856                 gtk_text_buffer_copy_clipboard(
9857                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9858                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9859 }
9860
9861 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9862                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9863 {
9864         if (GTK_IS_TEXT_VIEW(entry)) {
9865                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9866                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9867                 GtkTextIter start_iter, end_iter;
9868                 gint start, end;
9869                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9870
9871                 if (contents == NULL)
9872                         return;
9873         
9874                 /* we shouldn't delete the selection when middle-click-pasting, or we
9875                  * can't mid-click-paste our own selection */
9876                 if (clip != GDK_SELECTION_PRIMARY) {
9877                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9878                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9879                 }
9880                 
9881                 if (insert_place == NULL) {
9882                         /* if insert_place isn't specified, insert at the cursor.
9883                          * used for Ctrl-V pasting */
9884                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9885                         start = gtk_text_iter_get_offset(&start_iter);
9886                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9887                 } else {
9888                         /* if insert_place is specified, paste here.
9889                          * used for mid-click-pasting */
9890                         start = gtk_text_iter_get_offset(insert_place);
9891                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9892                         if (prefs_common.primary_paste_unselects)
9893                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9894                 }
9895                 
9896                 if (!wrap) {
9897                         /* paste unwrapped: mark the paste so it's not wrapped later */
9898                         end = start + strlen(contents);
9899                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9900                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9901                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9902                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9903                         /* rewrap paragraph now (after a mid-click-paste) */
9904                         mark_start = gtk_text_buffer_get_insert(buffer);
9905                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9906                         gtk_text_iter_backward_char(&start_iter);
9907                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9908                 }
9909         } else if (GTK_IS_EDITABLE(entry))
9910                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9911
9912         compose->modified = TRUE;
9913 }
9914
9915 static void entry_allsel(GtkWidget *entry)
9916 {
9917         if (GTK_IS_EDITABLE(entry))
9918                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9919         else if (GTK_IS_TEXT_VIEW(entry)) {
9920                 GtkTextIter startiter, enditer;
9921                 GtkTextBuffer *textbuf;
9922
9923                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9924                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9925                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9926
9927                 gtk_text_buffer_move_mark_by_name(textbuf, 
9928                         "selection_bound", &startiter);
9929                 gtk_text_buffer_move_mark_by_name(textbuf, 
9930                         "insert", &enditer);
9931         }
9932 }
9933
9934 static void compose_cut_cb(GtkAction *action, gpointer data)
9935 {
9936         Compose *compose = (Compose *)data;
9937         if (compose->focused_editable 
9938 #ifndef GENERIC_UMPC
9939             && gtkut_widget_has_focus(compose->focused_editable)
9940 #endif
9941             )
9942                 entry_cut_clipboard(compose->focused_editable);
9943 }
9944
9945 static void compose_copy_cb(GtkAction *action, gpointer data)
9946 {
9947         Compose *compose = (Compose *)data;
9948         if (compose->focused_editable 
9949 #ifndef GENERIC_UMPC
9950             && gtkut_widget_has_focus(compose->focused_editable)
9951 #endif
9952             )
9953                 entry_copy_clipboard(compose->focused_editable);
9954 }
9955
9956 static void compose_paste_cb(GtkAction *action, gpointer data)
9957 {
9958         Compose *compose = (Compose *)data;
9959         gint prev_autowrap;
9960         GtkTextBuffer *buffer;
9961         BLOCK_WRAP();
9962         if (compose->focused_editable &&
9963             gtkut_widget_has_focus(compose->focused_editable))
9964                 entry_paste_clipboard(compose, compose->focused_editable, 
9965                                 prefs_common.linewrap_pastes,
9966                                 GDK_SELECTION_CLIPBOARD, NULL);
9967         UNBLOCK_WRAP();
9968
9969 #ifdef USE_ENCHANT
9970         if (gtkut_widget_has_focus(compose->text) &&
9971             compose->gtkaspell && 
9972             compose->gtkaspell->check_while_typing)
9973                 gtkaspell_highlight_all(compose->gtkaspell);
9974 #endif
9975 }
9976
9977 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9978 {
9979         Compose *compose = (Compose *)data;
9980         gint wrap_quote = prefs_common.linewrap_quote;
9981         if (compose->focused_editable 
9982 #ifndef GENERIC_UMPC
9983             && gtkut_widget_has_focus(compose->focused_editable)
9984 #endif
9985             ) {
9986                 /* let text_insert() (called directly or at a later time
9987                  * after the gtk_editable_paste_clipboard) know that 
9988                  * text is to be inserted as a quotation. implemented
9989                  * by using a simple refcount... */
9990                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9991                                                 G_OBJECT(compose->focused_editable),
9992                                                 "paste_as_quotation"));
9993                 g_object_set_data(G_OBJECT(compose->focused_editable),
9994                                     "paste_as_quotation",
9995                                     GINT_TO_POINTER(paste_as_quotation + 1));
9996                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9997                 entry_paste_clipboard(compose, compose->focused_editable, 
9998                                 prefs_common.linewrap_pastes,
9999                                 GDK_SELECTION_CLIPBOARD, NULL);
10000                 prefs_common.linewrap_quote = wrap_quote;
10001         }
10002 }
10003
10004 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10005 {
10006         Compose *compose = (Compose *)data;
10007         gint prev_autowrap;
10008         GtkTextBuffer *buffer;
10009         BLOCK_WRAP();
10010         if (compose->focused_editable 
10011 #ifndef GENERIC_UMPC
10012             && gtkut_widget_has_focus(compose->focused_editable)
10013 #endif
10014             )
10015                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10016                         GDK_SELECTION_CLIPBOARD, NULL);
10017         UNBLOCK_WRAP();
10018
10019 #ifdef USE_ENCHANT
10020         if (gtkut_widget_has_focus(compose->text) &&
10021             compose->gtkaspell && 
10022             compose->gtkaspell->check_while_typing)
10023                 gtkaspell_highlight_all(compose->gtkaspell);
10024 #endif
10025 }
10026
10027 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10028 {
10029         Compose *compose = (Compose *)data;
10030         gint prev_autowrap;
10031         GtkTextBuffer *buffer;
10032         BLOCK_WRAP();
10033         if (compose->focused_editable 
10034 #ifndef GENERIC_UMPC
10035             && gtkut_widget_has_focus(compose->focused_editable)
10036 #endif
10037             )
10038                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10039                         GDK_SELECTION_CLIPBOARD, NULL);
10040         UNBLOCK_WRAP();
10041
10042 #ifdef USE_ENCHANT
10043         if (gtkut_widget_has_focus(compose->text) &&
10044             compose->gtkaspell &&
10045             compose->gtkaspell->check_while_typing)
10046                 gtkaspell_highlight_all(compose->gtkaspell);
10047 #endif
10048 }
10049
10050 static void compose_allsel_cb(GtkAction *action, gpointer data)
10051 {
10052         Compose *compose = (Compose *)data;
10053         if (compose->focused_editable 
10054 #ifndef GENERIC_UMPC
10055             && gtkut_widget_has_focus(compose->focused_editable)
10056 #endif
10057             )
10058                 entry_allsel(compose->focused_editable);
10059 }
10060
10061 static void textview_move_beginning_of_line (GtkTextView *text)
10062 {
10063         GtkTextBuffer *buffer;
10064         GtkTextMark *mark;
10065         GtkTextIter ins;
10066
10067         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10068
10069         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10070         mark = gtk_text_buffer_get_insert(buffer);
10071         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10072         gtk_text_iter_set_line_offset(&ins, 0);
10073         gtk_text_buffer_place_cursor(buffer, &ins);
10074 }
10075
10076 static void textview_move_forward_character (GtkTextView *text)
10077 {
10078         GtkTextBuffer *buffer;
10079         GtkTextMark *mark;
10080         GtkTextIter ins;
10081
10082         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10083
10084         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10085         mark = gtk_text_buffer_get_insert(buffer);
10086         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10087         if (gtk_text_iter_forward_cursor_position(&ins))
10088                 gtk_text_buffer_place_cursor(buffer, &ins);
10089 }
10090
10091 static void textview_move_backward_character (GtkTextView *text)
10092 {
10093         GtkTextBuffer *buffer;
10094         GtkTextMark *mark;
10095         GtkTextIter ins;
10096
10097         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10098
10099         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10100         mark = gtk_text_buffer_get_insert(buffer);
10101         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10102         if (gtk_text_iter_backward_cursor_position(&ins))
10103                 gtk_text_buffer_place_cursor(buffer, &ins);
10104 }
10105
10106 static void textview_move_forward_word (GtkTextView *text)
10107 {
10108         GtkTextBuffer *buffer;
10109         GtkTextMark *mark;
10110         GtkTextIter ins;
10111         gint count;
10112
10113         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10114
10115         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10116         mark = gtk_text_buffer_get_insert(buffer);
10117         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10118         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10119         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10120                 gtk_text_iter_backward_word_start(&ins);
10121                 gtk_text_buffer_place_cursor(buffer, &ins);
10122         }
10123 }
10124
10125 static void textview_move_backward_word (GtkTextView *text)
10126 {
10127         GtkTextBuffer *buffer;
10128         GtkTextMark *mark;
10129         GtkTextIter ins;
10130         gint count;
10131
10132         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10133
10134         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10135         mark = gtk_text_buffer_get_insert(buffer);
10136         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10137         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10138         if (gtk_text_iter_backward_word_starts(&ins, 1))
10139                 gtk_text_buffer_place_cursor(buffer, &ins);
10140 }
10141
10142 static void textview_move_end_of_line (GtkTextView *text)
10143 {
10144         GtkTextBuffer *buffer;
10145         GtkTextMark *mark;
10146         GtkTextIter ins;
10147
10148         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10149
10150         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10151         mark = gtk_text_buffer_get_insert(buffer);
10152         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10153         if (gtk_text_iter_forward_to_line_end(&ins))
10154                 gtk_text_buffer_place_cursor(buffer, &ins);
10155 }
10156
10157 static void textview_move_next_line (GtkTextView *text)
10158 {
10159         GtkTextBuffer *buffer;
10160         GtkTextMark *mark;
10161         GtkTextIter ins;
10162         gint offset;
10163
10164         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10165
10166         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10167         mark = gtk_text_buffer_get_insert(buffer);
10168         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10169         offset = gtk_text_iter_get_line_offset(&ins);
10170         if (gtk_text_iter_forward_line(&ins)) {
10171                 gtk_text_iter_set_line_offset(&ins, offset);
10172                 gtk_text_buffer_place_cursor(buffer, &ins);
10173         }
10174 }
10175
10176 static void textview_move_previous_line (GtkTextView *text)
10177 {
10178         GtkTextBuffer *buffer;
10179         GtkTextMark *mark;
10180         GtkTextIter ins;
10181         gint offset;
10182
10183         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10184
10185         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10186         mark = gtk_text_buffer_get_insert(buffer);
10187         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10188         offset = gtk_text_iter_get_line_offset(&ins);
10189         if (gtk_text_iter_backward_line(&ins)) {
10190                 gtk_text_iter_set_line_offset(&ins, offset);
10191                 gtk_text_buffer_place_cursor(buffer, &ins);
10192         }
10193 }
10194
10195 static void textview_delete_forward_character (GtkTextView *text)
10196 {
10197         GtkTextBuffer *buffer;
10198         GtkTextMark *mark;
10199         GtkTextIter ins, end_iter;
10200
10201         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10202
10203         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10204         mark = gtk_text_buffer_get_insert(buffer);
10205         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10206         end_iter = ins;
10207         if (gtk_text_iter_forward_char(&end_iter)) {
10208                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10209         }
10210 }
10211
10212 static void textview_delete_backward_character (GtkTextView *text)
10213 {
10214         GtkTextBuffer *buffer;
10215         GtkTextMark *mark;
10216         GtkTextIter ins, end_iter;
10217
10218         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10219
10220         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10221         mark = gtk_text_buffer_get_insert(buffer);
10222         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10223         end_iter = ins;
10224         if (gtk_text_iter_backward_char(&end_iter)) {
10225                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10226         }
10227 }
10228
10229 static void textview_delete_forward_word (GtkTextView *text)
10230 {
10231         GtkTextBuffer *buffer;
10232         GtkTextMark *mark;
10233         GtkTextIter ins, end_iter;
10234
10235         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10236
10237         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10238         mark = gtk_text_buffer_get_insert(buffer);
10239         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10240         end_iter = ins;
10241         if (gtk_text_iter_forward_word_end(&end_iter)) {
10242                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10243         }
10244 }
10245
10246 static void textview_delete_backward_word (GtkTextView *text)
10247 {
10248         GtkTextBuffer *buffer;
10249         GtkTextMark *mark;
10250         GtkTextIter ins, end_iter;
10251
10252         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10253
10254         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10255         mark = gtk_text_buffer_get_insert(buffer);
10256         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10257         end_iter = ins;
10258         if (gtk_text_iter_backward_word_start(&end_iter)) {
10259                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10260         }
10261 }
10262
10263 static void textview_delete_line (GtkTextView *text)
10264 {
10265         GtkTextBuffer *buffer;
10266         GtkTextMark *mark;
10267         GtkTextIter ins, start_iter, end_iter;
10268
10269         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10270
10271         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10272         mark = gtk_text_buffer_get_insert(buffer);
10273         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10274
10275         start_iter = ins;
10276         gtk_text_iter_set_line_offset(&start_iter, 0);
10277
10278         end_iter = ins;
10279         if (gtk_text_iter_ends_line(&end_iter)){
10280                 if (!gtk_text_iter_forward_char(&end_iter))
10281                         gtk_text_iter_backward_char(&start_iter);
10282         }
10283         else 
10284                 gtk_text_iter_forward_to_line_end(&end_iter);
10285         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10286 }
10287
10288 static void textview_delete_to_line_end (GtkTextView *text)
10289 {
10290         GtkTextBuffer *buffer;
10291         GtkTextMark *mark;
10292         GtkTextIter ins, end_iter;
10293
10294         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10295
10296         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10297         mark = gtk_text_buffer_get_insert(buffer);
10298         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10299         end_iter = ins;
10300         if (gtk_text_iter_ends_line(&end_iter))
10301                 gtk_text_iter_forward_char(&end_iter);
10302         else
10303                 gtk_text_iter_forward_to_line_end(&end_iter);
10304         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10305 }
10306
10307 #define DO_ACTION(name, act) {                                          \
10308         if(!strcmp(name, a_name)) {                                     \
10309                 return act;                                             \
10310         }                                                               \
10311 }
10312 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10313 {
10314         const gchar *a_name = gtk_action_get_name(action);
10315         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10316         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10317         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10318         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10319         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10320         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10321         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10322         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10323         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10324         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10325         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10326         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10327         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10328         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10329         return -1;
10330 }
10331
10332 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10333 {
10334         Compose *compose = (Compose *)data;
10335         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10336         ComposeCallAdvancedAction action = -1;
10337         
10338         action = compose_call_advanced_action_from_path(gaction);
10339
10340         static struct {
10341                 void (*do_action) (GtkTextView *text);
10342         } action_table[] = {
10343                 {textview_move_beginning_of_line},
10344                 {textview_move_forward_character},
10345                 {textview_move_backward_character},
10346                 {textview_move_forward_word},
10347                 {textview_move_backward_word},
10348                 {textview_move_end_of_line},
10349                 {textview_move_next_line},
10350                 {textview_move_previous_line},
10351                 {textview_delete_forward_character},
10352                 {textview_delete_backward_character},
10353                 {textview_delete_forward_word},
10354                 {textview_delete_backward_word},
10355                 {textview_delete_line},
10356                 {textview_delete_to_line_end}
10357         };
10358
10359         if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10360
10361         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10362             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10363                 if (action_table[action].do_action)
10364                         action_table[action].do_action(text);
10365                 else
10366                         g_warning("Not implemented yet.");
10367         }
10368 }
10369
10370 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10371 {
10372         gchar *str = NULL;
10373         
10374         if (GTK_IS_EDITABLE(widget)) {
10375                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10376                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10377                         strlen(str));
10378                 g_free(str);
10379                 if (widget->parent && widget->parent->parent
10380                  && widget->parent->parent->parent) {
10381                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10382                                 gint y = widget->allocation.y;
10383                                 gint height = widget->allocation.height;
10384                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10385                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10386
10387                                 if (y < (int)shown->value) {
10388                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10389                                 }
10390                                 if (y + height > (int)shown->value + (int)shown->page_size) {
10391                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10392                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10393                                                         y + height - (int)shown->page_size - 1);
10394                                         } else {
10395                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10396                                                         (int)shown->upper - (int)shown->page_size - 1);
10397                                         }
10398                                 }
10399                         }
10400                 }
10401         }
10402
10403         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10404                 compose->focused_editable = widget;
10405         
10406 #ifdef GENERIC_UMPC
10407         if (GTK_IS_TEXT_VIEW(widget) 
10408             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10409                 g_object_ref(compose->notebook);
10410                 g_object_ref(compose->edit_vbox);
10411                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10412                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10413                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10414                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10415                 g_object_unref(compose->notebook);
10416                 g_object_unref(compose->edit_vbox);
10417                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10418                                         G_CALLBACK(compose_grab_focus_cb),
10419                                         compose);
10420                 gtk_widget_grab_focus(widget);
10421                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10422                                         G_CALLBACK(compose_grab_focus_cb),
10423                                         compose);
10424         } else if (!GTK_IS_TEXT_VIEW(widget) 
10425                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10426                 g_object_ref(compose->notebook);
10427                 g_object_ref(compose->edit_vbox);
10428                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10429                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10430                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10431                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10432                 g_object_unref(compose->notebook);
10433                 g_object_unref(compose->edit_vbox);
10434                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10435                                         G_CALLBACK(compose_grab_focus_cb),
10436                                         compose);
10437                 gtk_widget_grab_focus(widget);
10438                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10439                                         G_CALLBACK(compose_grab_focus_cb),
10440                                         compose);
10441         }
10442 #endif
10443 }
10444
10445 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10446 {
10447         compose->modified = TRUE;
10448 //      compose_beautify_paragraph(compose, NULL, TRUE);
10449 #ifndef GENERIC_UMPC
10450         compose_set_title(compose);
10451 #endif
10452 }
10453
10454 static void compose_wrap_cb(GtkAction *action, gpointer data)
10455 {
10456         Compose *compose = (Compose *)data;
10457         compose_beautify_paragraph(compose, NULL, TRUE);
10458 }
10459
10460 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10461 {
10462         Compose *compose = (Compose *)data;
10463         compose_wrap_all_full(compose, TRUE);
10464 }
10465
10466 static void compose_find_cb(GtkAction *action, gpointer data)
10467 {
10468         Compose *compose = (Compose *)data;
10469
10470         message_search_compose(compose);
10471 }
10472
10473 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10474                                          gpointer        data)
10475 {
10476         Compose *compose = (Compose *)data;
10477         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10478         if (compose->autowrap)
10479                 compose_wrap_all_full(compose, TRUE);
10480         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10481 }
10482
10483 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10484                                          gpointer        data)
10485 {
10486         Compose *compose = (Compose *)data;
10487         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10488 }
10489
10490 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10491 {
10492         Compose *compose = (Compose *)data;
10493
10494         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10495 }
10496
10497 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10498 {
10499         Compose *compose = (Compose *)data;
10500
10501         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10502 }
10503
10504 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10505 {
10506         g_free(compose->privacy_system);
10507
10508         compose->privacy_system = g_strdup(account->default_privacy_system);
10509         compose_update_privacy_system_menu_item(compose, warn);
10510 }
10511
10512 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10513 {
10514         Compose *compose = (Compose *)data;
10515
10516         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10517                 gtk_widget_show(compose->ruler_hbox);
10518                 prefs_common.show_ruler = TRUE;
10519         } else {
10520                 gtk_widget_hide(compose->ruler_hbox);
10521                 gtk_widget_queue_resize(compose->edit_vbox);
10522                 prefs_common.show_ruler = FALSE;
10523         }
10524 }
10525
10526 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10527                                              GdkDragContext     *context,
10528                                              gint                x,
10529                                              gint                y,
10530                                              GtkSelectionData   *data,
10531                                              guint               info,
10532                                              guint               time,
10533                                              gpointer            user_data)
10534 {
10535         Compose *compose = (Compose *)user_data;
10536         GList *list, *tmp;
10537
10538         if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10539 #ifdef G_OS_WIN32
10540          || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10541 #endif
10542            ) && gtk_drag_get_source_widget(context) != 
10543                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10544                 list = uri_list_extract_filenames((const gchar *)data->data);
10545                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10546                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10547                         compose_attach_append
10548                                 (compose, (const gchar *)tmp->data,
10549                                  utf8_filename, NULL);
10550                         g_free(utf8_filename);
10551                 }
10552                 if (list) compose_changed_cb(NULL, compose);
10553                 list_free_strings(list);
10554                 g_list_free(list);
10555         } else if (gtk_drag_get_source_widget(context) 
10556                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10557                 /* comes from our summaryview */
10558                 SummaryView * summaryview = NULL;
10559                 GSList * list = NULL, *cur = NULL;
10560                 
10561                 if (mainwindow_get_mainwindow())
10562                         summaryview = mainwindow_get_mainwindow()->summaryview;
10563                 
10564                 if (summaryview)
10565                         list = summary_get_selected_msg_list(summaryview);
10566                 
10567                 for (cur = list; cur; cur = cur->next) {
10568                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10569                         gchar *file = NULL;
10570                         if (msginfo)
10571                                 file = procmsg_get_message_file_full(msginfo, 
10572                                         TRUE, TRUE);
10573                         if (file) {
10574                                 compose_attach_append(compose, (const gchar *)file, 
10575                                         (const gchar *)file, "message/rfc822");
10576                                 g_free(file);
10577                         }
10578                 }
10579                 g_slist_free(list);
10580         }
10581 }
10582
10583 static gboolean compose_drag_drop(GtkWidget *widget,
10584                                   GdkDragContext *drag_context,
10585                                   gint x, gint y,
10586                                   guint time, gpointer user_data)
10587 {
10588         /* not handling this signal makes compose_insert_drag_received_cb
10589          * called twice */
10590         return TRUE;                                     
10591 }
10592
10593 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10594                                              GdkDragContext     *drag_context,
10595                                              gint                x,
10596                                              gint                y,
10597                                              GtkSelectionData   *data,
10598                                              guint               info,
10599                                              guint               time,
10600                                              gpointer            user_data)
10601 {
10602         Compose *compose = (Compose *)user_data;
10603         GList *list, *tmp;
10604
10605         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10606          * does not work */
10607 #ifndef G_OS_WIN32
10608         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10609 #else
10610         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10611 #endif
10612                 AlertValue val = G_ALERTDEFAULT;
10613
10614                 list = uri_list_extract_filenames((const gchar *)data->data);
10615                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10616                         /* Assume a list of no files, and data has ://, is a remote link */
10617                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10618                         gchar *tmpfile = get_tmp_file();
10619                         str_write_to_file(tmpdata, tmpfile);
10620                         g_free(tmpdata);  
10621                         compose_insert_file(compose, tmpfile);
10622                         claws_unlink(tmpfile);
10623                         g_free(tmpfile);
10624                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10625                         compose_beautify_paragraph(compose, NULL, TRUE);
10626                         return;
10627                 }
10628                 switch (prefs_common.compose_dnd_mode) {
10629                         case COMPOSE_DND_ASK:
10630                                 val = alertpanel_full(_("Insert or attach?"),
10631                                          _("Do you want to insert the contents of the file(s) "
10632                                            "into the message body, or attach it to the email?"),
10633                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10634                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10635                                 break;
10636                         case COMPOSE_DND_INSERT:
10637                                 val = G_ALERTALTERNATE;
10638                                 break;
10639                         case COMPOSE_DND_ATTACH:
10640                                 val = G_ALERTOTHER;
10641                                 break;
10642                         default:
10643                                 /* unexpected case */
10644                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10645                 }
10646
10647                 if (val & G_ALERTDISABLE) {
10648                         val &= ~G_ALERTDISABLE;
10649                         /* remember what action to perform by default, only if we don't click Cancel */
10650                         if (val == G_ALERTALTERNATE)
10651                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10652                         else if (val == G_ALERTOTHER)
10653                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10654                 }
10655
10656                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10657                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10658                         list_free_strings(list);
10659                         g_list_free(list);
10660                         return;
10661                 } else if (val == G_ALERTOTHER) {
10662                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10663                         list_free_strings(list);
10664                         g_list_free(list);
10665                         return;
10666                 } 
10667
10668                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10669                         compose_insert_file(compose, (const gchar *)tmp->data);
10670                 }
10671                 list_free_strings(list);
10672                 g_list_free(list);
10673                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10674                 return;
10675         } else {
10676                 return;
10677         }
10678         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10679 }
10680
10681 static void compose_header_drag_received_cb (GtkWidget          *widget,
10682                                              GdkDragContext     *drag_context,
10683                                              gint                x,
10684                                              gint                y,
10685                                              GtkSelectionData   *data,
10686                                              guint               info,
10687                                              guint               time,
10688                                              gpointer            user_data)
10689 {
10690         GtkEditable *entry = (GtkEditable *)user_data;
10691         gchar *email = (gchar *)data->data;
10692
10693         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10694          * does not work */
10695
10696         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10697                 gchar *decoded=g_new(gchar, strlen(email));
10698                 int start = 0;
10699
10700                 email += strlen("mailto:");
10701                 decode_uri(decoded, email); /* will fit */
10702                 gtk_editable_delete_text(entry, 0, -1);
10703                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10704                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10705                 g_free(decoded);
10706                 return;
10707         }
10708         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10709 }
10710
10711 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10712 {
10713         Compose *compose = (Compose *)data;
10714
10715         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10716                 compose->return_receipt = TRUE;
10717         else
10718                 compose->return_receipt = FALSE;
10719 }
10720
10721 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10722 {
10723         Compose *compose = (Compose *)data;
10724
10725         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10726                 compose->remove_references = TRUE;
10727         else
10728                 compose->remove_references = FALSE;
10729 }
10730
10731 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10732                                         ComposeHeaderEntry *headerentry)
10733 {
10734         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10735         return FALSE;
10736 }
10737
10738 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10739                                             GdkEventKey *event,
10740                                             ComposeHeaderEntry *headerentry)
10741 {
10742         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10743             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10744             !(event->state & GDK_MODIFIER_MASK) &&
10745             (event->keyval == GDK_BackSpace) &&
10746             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10747                 gtk_container_remove
10748                         (GTK_CONTAINER(headerentry->compose->header_table),
10749                          headerentry->combo);
10750                 gtk_container_remove
10751                         (GTK_CONTAINER(headerentry->compose->header_table),
10752                          headerentry->entry);
10753                 headerentry->compose->header_list =
10754                         g_slist_remove(headerentry->compose->header_list,
10755                                        headerentry);
10756                 g_free(headerentry);
10757         } else  if (event->keyval == GDK_Tab) {
10758                 if (headerentry->compose->header_last == headerentry) {
10759                         /* Override default next focus, and give it to subject_entry
10760                          * instead of notebook tabs
10761                          */
10762                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10763                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10764                         return TRUE;
10765                 }
10766         }
10767         return FALSE;
10768 }
10769
10770 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10771                                     ComposeHeaderEntry *headerentry)
10772 {
10773         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10774                 compose_create_header_entry(headerentry->compose);
10775                 g_signal_handlers_disconnect_matched
10776                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10777                          0, 0, NULL, NULL, headerentry);
10778                 
10779                 /* Automatically scroll down */
10780                 GTK_EVENTS_FLUSH();
10781                 compose_show_first_last_header(headerentry->compose, FALSE);
10782                 
10783         }
10784         return FALSE;
10785 }
10786
10787 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10788 {
10789         GtkAdjustment *vadj;
10790
10791         cm_return_if_fail(compose);
10792         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10793         cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10794         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10795         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10796         gtk_adjustment_changed(vadj);
10797 }
10798
10799 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10800                           const gchar *text, gint len, Compose *compose)
10801 {
10802         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10803                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10804         GtkTextMark *mark;
10805
10806         cm_return_if_fail(text != NULL);
10807
10808         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10809                                         G_CALLBACK(text_inserted),
10810                                         compose);
10811         if (paste_as_quotation) {
10812                 gchar *new_text;
10813                 const gchar *qmark;
10814                 guint pos = 0;
10815                 GtkTextIter start_iter;
10816
10817                 if (len < 0)
10818                         len = strlen(text);
10819
10820                 new_text = g_strndup(text, len);
10821
10822                 qmark = compose_quote_char_from_context(compose);
10823
10824                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10825                 gtk_text_buffer_place_cursor(buffer, iter);
10826
10827                 pos = gtk_text_iter_get_offset(iter);
10828
10829                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10830                                                   _("Quote format error at line %d."));
10831                 quote_fmt_reset_vartable();
10832                 g_free(new_text);
10833                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10834                                   GINT_TO_POINTER(paste_as_quotation - 1));
10835                                   
10836                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10837                 gtk_text_buffer_place_cursor(buffer, iter);
10838                 gtk_text_buffer_delete_mark(buffer, mark);
10839
10840                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10841                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10842                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10843                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10844                 gtk_text_buffer_delete_mark(buffer, mark);
10845         } else {
10846                 if (strcmp(text, "\n") || compose->automatic_break
10847                 || gtk_text_iter_starts_line(iter)) {
10848                         GtkTextIter before_ins;
10849                         gtk_text_buffer_insert(buffer, iter, text, len);
10850                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10851                                 before_ins = *iter; 
10852                                 gtk_text_iter_backward_chars(&before_ins, len);
10853                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10854                         }
10855                 } else {
10856                         /* check if the preceding is just whitespace or quote */
10857                         GtkTextIter start_line;
10858                         gchar *tmp = NULL, *quote = NULL;
10859                         gint quote_len = 0, is_normal = 0;
10860                         start_line = *iter;
10861                         gtk_text_iter_set_line_offset(&start_line, 0); 
10862                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10863                         g_strstrip(tmp);
10864
10865                         if (*tmp == '\0') {
10866                                 is_normal = 1;
10867                         } else {
10868                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10869                                 if (quote)
10870                                         is_normal = 1;
10871                                 g_free(quote);
10872                         }
10873                         g_free(tmp);
10874                         
10875                         if (is_normal) {
10876                                 gtk_text_buffer_insert(buffer, iter, text, len);
10877                         } else {
10878                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10879                                         iter, text, len, "no_join", NULL);
10880                         }
10881                 }
10882         }
10883         
10884         if (!paste_as_quotation) {
10885                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10886                 compose_beautify_paragraph(compose, iter, FALSE);
10887                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10888                 gtk_text_buffer_delete_mark(buffer, mark);
10889         }
10890
10891         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10892                                           G_CALLBACK(text_inserted),
10893                                           compose);
10894         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10895
10896         if (prefs_common.autosave && 
10897             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10898             compose->draft_timeout_tag != -2 /* disabled while loading */)
10899                 compose->draft_timeout_tag = g_timeout_add
10900                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10901 }
10902 static gint compose_defer_auto_save_draft(Compose *compose)
10903 {
10904         compose->draft_timeout_tag = -1;
10905         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10906         return FALSE;
10907 }
10908
10909 #if USE_ENCHANT
10910 static void compose_check_all(GtkAction *action, gpointer data)
10911 {
10912         Compose *compose = (Compose *)data;
10913         if (!compose->gtkaspell)
10914                 return;
10915                 
10916         if (gtkut_widget_has_focus(compose->subject_entry))
10917                 claws_spell_entry_check_all(
10918                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
10919         else
10920                 gtkaspell_check_all(compose->gtkaspell);
10921 }
10922
10923 static void compose_highlight_all(GtkAction *action, gpointer data)
10924 {
10925         Compose *compose = (Compose *)data;
10926         if (compose->gtkaspell) {
10927                 claws_spell_entry_recheck_all(
10928                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10929                 gtkaspell_highlight_all(compose->gtkaspell);
10930         }
10931 }
10932
10933 static void compose_check_backwards(GtkAction *action, gpointer data)
10934 {
10935         Compose *compose = (Compose *)data;
10936         if (!compose->gtkaspell) {
10937                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10938                 return;
10939         }
10940
10941         if (gtkut_widget_has_focus(compose->subject_entry))
10942                 claws_spell_entry_check_backwards(
10943                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10944         else
10945                 gtkaspell_check_backwards(compose->gtkaspell);
10946 }
10947
10948 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10949 {
10950         Compose *compose = (Compose *)data;
10951         if (!compose->gtkaspell) {
10952                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10953                 return;
10954         }
10955
10956         if (gtkut_widget_has_focus(compose->subject_entry))
10957                 claws_spell_entry_check_forwards_go(
10958                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10959         else
10960                 gtkaspell_check_forwards_go(compose->gtkaspell);
10961 }
10962 #endif
10963
10964 /*!
10965  *\brief        Guess originating forward account from MsgInfo and several 
10966  *              "common preference" settings. Return NULL if no guess. 
10967  */
10968 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10969 {
10970         PrefsAccount *account = NULL;
10971         
10972         cm_return_val_if_fail(msginfo, NULL);
10973         cm_return_val_if_fail(msginfo->folder, NULL);
10974         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10975
10976         if (msginfo->folder->prefs->enable_default_account)
10977                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10978                 
10979         if (!account) 
10980                 account = msginfo->folder->folder->account;
10981                 
10982         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10983                 gchar *to;
10984                 Xstrdup_a(to, msginfo->to, return NULL);
10985                 extract_address(to);
10986                 account = account_find_from_address(to, FALSE);
10987         }
10988
10989         if (!account && prefs_common.forward_account_autosel) {
10990                 gchar cc[BUFFSIZE];
10991                 if (!procheader_get_header_from_msginfo
10992                         (msginfo, cc,sizeof cc , "Cc:")) { 
10993                         gchar *buf = cc + strlen("Cc:");
10994                         extract_address(buf);
10995                         account = account_find_from_address(buf, FALSE);
10996                 }
10997         }
10998         
10999         if (!account && prefs_common.forward_account_autosel) {
11000                 gchar deliveredto[BUFFSIZE];
11001                 if (!procheader_get_header_from_msginfo
11002                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11003                         gchar *buf = deliveredto + strlen("Delivered-To:");
11004                         extract_address(buf);
11005                         account = account_find_from_address(buf, FALSE);
11006                 }
11007         }
11008         
11009         return account;
11010 }
11011
11012 gboolean compose_close(Compose *compose)
11013 {
11014         gint x, y;
11015
11016         if (!g_mutex_trylock(compose->mutex)) {
11017                 /* we have to wait for the (possibly deferred by auto-save)
11018                  * drafting to be done, before destroying the compose under
11019                  * it. */
11020                 debug_print("waiting for drafting to finish...\n");
11021                 compose_allow_user_actions(compose, FALSE);
11022                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11023                 return FALSE;
11024         }
11025         cm_return_val_if_fail(compose, FALSE);
11026         gtkut_widget_get_uposition(compose->window, &x, &y);
11027         if (!compose->batch) {
11028                 prefs_common.compose_x = x;
11029                 prefs_common.compose_y = y;
11030         }
11031         g_mutex_unlock(compose->mutex);
11032         compose_destroy(compose);
11033         return FALSE;
11034 }
11035
11036 /**
11037  * Add entry field for each address in list.
11038  * \param compose     E-Mail composition object.
11039  * \param listAddress List of (formatted) E-Mail addresses.
11040  */
11041 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11042         GList *node;
11043         gchar *addr;
11044         node = listAddress;
11045         while( node ) {
11046                 addr = ( gchar * ) node->data;
11047                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11048                 node = g_list_next( node );
11049         }
11050 }
11051
11052 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11053                                     guint action, gboolean opening_multiple)
11054 {
11055         gchar *body = NULL;
11056         GSList *new_msglist = NULL;
11057         MsgInfo *tmp_msginfo = NULL;
11058         gboolean originally_enc = FALSE;
11059         gboolean originally_sig = FALSE;
11060         Compose *compose = NULL;
11061         gchar *s_system = NULL;
11062
11063         cm_return_if_fail(msgview != NULL);
11064
11065         cm_return_if_fail(msginfo_list != NULL);
11066
11067         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11068                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11069                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11070
11071                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11072                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11073                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11074                                                 orig_msginfo, mimeinfo);
11075                         if (tmp_msginfo != NULL) {
11076                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11077
11078                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11079                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11080                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11081
11082                                 tmp_msginfo->folder = orig_msginfo->folder;
11083                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11084                                 if (orig_msginfo->tags) {
11085                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11086                                         tmp_msginfo->folder->tags_dirty = TRUE;
11087                                 }
11088                         }
11089                 }
11090         }
11091
11092         if (!opening_multiple)
11093                 body = messageview_get_selection(msgview);
11094
11095         if (new_msglist) {
11096                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11097                 procmsg_msginfo_free(tmp_msginfo);
11098                 g_slist_free(new_msglist);
11099         } else
11100                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11101
11102         if (compose && originally_enc) {
11103                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11104         }
11105
11106         if (compose && originally_sig && compose->account->default_sign_reply) {
11107                 compose_force_signing(compose, compose->account, s_system);
11108         }
11109         g_free(s_system);
11110         g_free(body);
11111         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11112 }
11113
11114 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11115                                     guint action)
11116 {
11117         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11118         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11119                 GSList *cur = msginfo_list;
11120                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11121                                                "messages. Opening the windows "
11122                                                "could take some time. Do you "
11123                                                "want to continue?"), 
11124                                                g_slist_length(msginfo_list));
11125                 if (g_slist_length(msginfo_list) > 9
11126                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11127                     != G_ALERTALTERNATE) {
11128                         g_free(msg);
11129                         return;
11130                 }
11131                 g_free(msg);
11132                 /* We'll open multiple compose windows */
11133                 /* let the WM place the next windows */
11134                 compose_force_window_origin = FALSE;
11135                 for (; cur; cur = cur->next) {
11136                         GSList tmplist;
11137                         tmplist.data = cur->data;
11138                         tmplist.next = NULL;
11139                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11140                 }
11141                 compose_force_window_origin = TRUE;
11142         } else {
11143                 /* forwarding multiple mails as attachments is done via a
11144                  * single compose window */
11145                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11146         }
11147 }
11148
11149 void compose_check_for_email_account(Compose *compose)
11150 {
11151         PrefsAccount *ac = NULL, *curr = NULL;
11152         GList *list;
11153         
11154         if (!compose)
11155                 return;
11156
11157         if (compose->account && compose->account->protocol == A_NNTP) {
11158                 ac = account_get_cur_account();
11159                 if (ac->protocol == A_NNTP) {
11160                         list = account_get_list();
11161                         
11162                         for( ; list != NULL ; list = g_list_next(list)) {
11163                                 curr = (PrefsAccount *) list->data;
11164                                 if (curr->protocol != A_NNTP) {
11165                                         ac = curr;
11166                                         break;
11167                                 }
11168                         }
11169                 }
11170                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11171                                         ac->account_id); 
11172         }
11173 }
11174
11175 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11176                                 const gchar *address)
11177 {
11178         GSList *msginfo_list = NULL;
11179         gchar *body =  messageview_get_selection(msgview);
11180         Compose *compose;
11181         
11182         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11183         
11184         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11185         compose_check_for_email_account(compose);
11186         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11187         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11188         compose_reply_set_subject(compose, msginfo);
11189
11190         g_free(body);
11191         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11192 }
11193
11194 void compose_set_position(Compose *compose, gint pos)
11195 {
11196         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11197
11198         gtkut_text_view_set_position(text, pos);
11199 }
11200
11201 gboolean compose_search_string(Compose *compose,
11202                                 const gchar *str, gboolean case_sens)
11203 {
11204         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11205
11206         return gtkut_text_view_search_string(text, str, case_sens);
11207 }
11208
11209 gboolean compose_search_string_backward(Compose *compose,
11210                                 const gchar *str, gboolean case_sens)
11211 {
11212         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11213
11214         return gtkut_text_view_search_string_backward(text, str, case_sens);
11215 }
11216
11217 /* allocate a msginfo structure and populate its data from a compose data structure */
11218 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11219 {
11220         MsgInfo *newmsginfo;
11221         GSList *list;
11222         gchar buf[BUFFSIZE];
11223
11224         cm_return_val_if_fail( compose != NULL, NULL );
11225
11226         newmsginfo = procmsg_msginfo_new();
11227
11228         /* date is now */
11229         get_rfc822_date(buf, sizeof(buf));
11230         newmsginfo->date = g_strdup(buf);
11231
11232         /* from */
11233         if (compose->from_name) {
11234                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11235                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11236         }
11237
11238         /* subject */
11239         if (compose->subject_entry)
11240                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11241
11242         /* to, cc, reply-to, newsgroups */
11243         for (list = compose->header_list; list; list = list->next) {
11244                 gchar *header = gtk_editable_get_chars(
11245                                                                 GTK_EDITABLE(
11246                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11247                 gchar *entry = gtk_editable_get_chars(
11248                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11249
11250                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11251                         if ( newmsginfo->to == NULL ) {
11252                                 newmsginfo->to = g_strdup(entry);
11253                         } else if (entry && *entry) {
11254                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11255                                 g_free(newmsginfo->to);
11256                                 newmsginfo->to = tmp;
11257                         }
11258                 } else
11259                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11260                         if ( newmsginfo->cc == NULL ) {
11261                                 newmsginfo->cc = g_strdup(entry);
11262                         } else if (entry && *entry) {
11263                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11264                                 g_free(newmsginfo->cc);
11265                                 newmsginfo->cc = tmp;
11266                         }
11267                 } else
11268                 if ( strcasecmp(header,
11269                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11270                         if ( newmsginfo->newsgroups == NULL ) {
11271                                 newmsginfo->newsgroups = g_strdup(entry);
11272                         } else if (entry && *entry) {
11273                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11274                                 g_free(newmsginfo->newsgroups);
11275                                 newmsginfo->newsgroups = tmp;
11276                         }
11277                 }
11278
11279                 g_free(header);
11280                 g_free(entry);  
11281         }
11282
11283         /* other data is unset */
11284
11285         return newmsginfo;
11286 }
11287
11288 #ifdef USE_ENCHANT
11289 /* update compose's dictionaries from folder dict settings */
11290 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11291                                                 FolderItem *folder_item)
11292 {
11293         cm_return_if_fail(compose != NULL);
11294
11295         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11296                 FolderItemPrefs *prefs = folder_item->prefs;
11297
11298                 if (prefs->enable_default_dictionary)
11299                         gtkaspell_change_dict(compose->gtkaspell,
11300                                         prefs->default_dictionary, FALSE);
11301                 if (folder_item->prefs->enable_default_alt_dictionary)
11302                         gtkaspell_change_alt_dict(compose->gtkaspell,
11303                                         prefs->default_alt_dictionary);
11304                 if (prefs->enable_default_dictionary
11305                         || prefs->enable_default_alt_dictionary)
11306                         compose_spell_menu_changed(compose);
11307         }
11308 }
11309 #endif
11310
11311 /*
11312  * End of Source.
11313  */