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