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