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