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