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