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