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