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