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