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