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