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