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