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