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