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