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