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