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