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