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