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