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