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