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