b5cec4f43166da86e1932453ff252bdb4035cd9c
[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 #define IS_IN_CUSTOM_HEADER(header) \
6133         (compose->account->add_customhdr && \
6134          custom_header_find(compose->account->customhdr_list, header) != NULL)
6135
6136 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6137                                                     GString *header, 
6138                                                     const gchar *fieldname,
6139                                                     const gchar *seperator)
6140 {
6141         gchar *str, *fieldname_w_colon;
6142         gboolean add_field = FALSE;
6143         GSList *list;
6144         ComposeHeaderEntry *headerentry;
6145         const gchar *headerentryname;
6146         const gchar *trans_fieldname;
6147         GString *fieldstr;
6148
6149         if (IS_IN_CUSTOM_HEADER(fieldname))
6150                 return;
6151
6152         debug_print("Adding %s-fields\n", fieldname);
6153
6154         fieldstr = g_string_sized_new(64);
6155
6156         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6157         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6158
6159         for (list = compose->header_list; list; list = list->next) {
6160                 headerentry = ((ComposeHeaderEntry *)list->data);
6161                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6162
6163                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6164                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6165                         g_strstrip(str);
6166                         if (str[0] != '\0') {
6167                                 if (add_field)
6168                                         g_string_append(fieldstr, seperator);
6169                                 g_string_append(fieldstr, str);
6170                                 add_field = TRUE;
6171                         }
6172                         g_free(str);
6173                 }
6174         }
6175         if (add_field) {
6176                 gchar *buf;
6177
6178                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6179                 compose_convert_header
6180                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6181                         strlen(fieldname) + 2, TRUE);
6182                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6183                 g_free(buf);
6184         }
6185
6186         g_free(fieldname_w_colon);
6187         g_string_free(fieldstr, TRUE);
6188
6189         return;
6190 }
6191
6192 static gchar *compose_get_manual_headers_info(Compose *compose)
6193 {
6194         GString *sh_header = g_string_new(" ");
6195         GSList *list;
6196         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6197
6198         for (list = compose->header_list; list; list = list->next) {
6199                 ComposeHeaderEntry *headerentry;
6200                 gchar *tmp;
6201                 gchar *headername;
6202                 gchar *headername_wcolon;
6203                 const gchar *headername_trans;
6204                 gchar **string;
6205                 gboolean standard_header = FALSE;
6206
6207                 headerentry = ((ComposeHeaderEntry *)list->data);
6208
6209                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6210                 g_strstrip(tmp);
6211                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6212                         g_free(tmp);
6213                         continue;
6214                 }
6215
6216                 if (!strstr(tmp, ":")) {
6217                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6218                         headername = g_strdup(tmp);
6219                 } else {
6220                         headername_wcolon = g_strdup(tmp);
6221                         headername = g_strdup(strtok(tmp, ":"));
6222                 }
6223                 g_free(tmp);
6224                 
6225                 string = std_headers;
6226                 while (*string != NULL) {
6227                         headername_trans = prefs_common_translated_header_name(*string);
6228                         if (!strcmp(headername_trans, headername_wcolon))
6229                                 standard_header = TRUE;
6230                         string++;
6231                 }
6232                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6233                         g_string_append_printf(sh_header, "%s ", headername);
6234                 g_free(headername);
6235                 g_free(headername_wcolon);
6236         }
6237         g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6238         return g_string_free(sh_header, FALSE);
6239 }
6240
6241 static gchar *compose_get_header(Compose *compose)
6242 {
6243         gchar buf[BUFFSIZE];
6244         const gchar *entry_str;
6245         gchar *str;
6246         gchar *name;
6247         GSList *list;
6248         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6249         GString *header;
6250         gchar *from_name = NULL, *from_address = NULL;
6251         gchar *tmp;
6252
6253         cm_return_val_if_fail(compose->account != NULL, NULL);
6254         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6255
6256         header = g_string_sized_new(64);
6257
6258         /* Date */
6259         get_rfc822_date(buf, sizeof(buf));
6260         g_string_append_printf(header, "Date: %s\n", buf);
6261
6262         /* From */
6263         
6264         if (compose->account->name && *compose->account->name) {
6265                 gchar *buf;
6266                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6267                 tmp = g_strdup_printf("%s <%s>",
6268                         buf, compose->account->address);
6269         } else {
6270                 tmp = g_strdup_printf("%s",
6271                         compose->account->address);
6272         }
6273         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6274         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6275                 /* use default */
6276                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6277                 from_address = g_strdup(compose->account->address);
6278         } else {
6279                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6280                 /* extract name and address */
6281                 if (strstr(spec, " <") && strstr(spec, ">")) {
6282                         from_address = g_strdup(strrchr(spec, '<')+1);
6283                         *(strrchr(from_address, '>')) = '\0';
6284                         from_name = g_strdup(spec);
6285                         *(strrchr(from_name, '<')) = '\0';
6286                 } else {
6287                         from_name = NULL;
6288                         from_address = g_strdup(spec);
6289                 }
6290                 g_free(spec);
6291         }
6292         g_free(tmp);
6293         
6294         
6295         if (from_name && *from_name) {
6296                 compose_convert_header
6297                         (compose, buf, sizeof(buf), from_name,
6298                          strlen("From: "), TRUE);
6299                 QUOTE_IF_REQUIRED(name, buf);
6300                 
6301                 g_string_append_printf(header, "From: %s <%s>\n",
6302                         name, from_address);
6303         } else
6304                 g_string_append_printf(header, "From: %s\n", from_address);
6305         
6306         g_free(from_name);
6307         g_free(from_address);
6308
6309         /* To */
6310         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6311
6312         /* Newsgroups */
6313         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6314
6315         /* Cc */
6316         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6317
6318         /* Bcc */
6319         /* 
6320          * If this account is a NNTP account remove Bcc header from 
6321          * message body since it otherwise will be publicly shown
6322          */
6323         if (compose->account->protocol != A_NNTP)
6324                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6325
6326         /* Subject */
6327         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6328
6329         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6330                 g_strstrip(str);
6331                 if (*str != '\0') {
6332                         compose_convert_header(compose, buf, sizeof(buf), str,
6333                                                strlen("Subject: "), FALSE);
6334                         g_string_append_printf(header, "Subject: %s\n", buf);
6335                 }
6336         }
6337         g_free(str);
6338
6339         /* Message-ID */
6340         if (compose->account->set_domain && compose->account->domain) {
6341                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
6342         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6343                 g_snprintf(buf, sizeof(buf), "%s", 
6344                         strchr(compose->account->address, '@') ?
6345                                 strchr(compose->account->address, '@')+1 :
6346                                 compose->account->address);
6347         } else {
6348                 g_snprintf(buf, sizeof(buf), "%s", "");
6349         }
6350         
6351         if (compose->account->gen_msgid) {
6352                 gchar *addr = NULL;
6353                 if (compose->account->msgid_with_addr) {
6354                         addr = compose->account->address;
6355                 }
6356                 generate_msgid(buf, sizeof(buf), addr);
6357                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6358                 compose->msgid = g_strdup(buf);
6359         } else {
6360                 compose->msgid = NULL;
6361         }
6362
6363         if (compose->remove_references == FALSE) {
6364                 /* In-Reply-To */
6365                 if (compose->inreplyto && compose->to_list)
6366                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6367         
6368                 /* References */
6369                 if (compose->references)
6370                         g_string_append_printf(header, "References: %s\n", compose->references);
6371         }
6372
6373         /* Followup-To */
6374         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6375
6376         /* Reply-To */
6377         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6378
6379         /* Organization */
6380         if (compose->account->organization &&
6381             strlen(compose->account->organization) &&
6382             !IS_IN_CUSTOM_HEADER("Organization")) {
6383                 compose_convert_header(compose, buf, sizeof(buf),
6384                                        compose->account->organization,
6385                                        strlen("Organization: "), FALSE);
6386                 g_string_append_printf(header, "Organization: %s\n", buf);
6387         }
6388
6389         /* Program version and system info */
6390         if (compose->account->gen_xmailer &&
6391             g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6392             !compose->newsgroup_list) {
6393                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6394                         prog_version,
6395                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6396                         TARGET_ALIAS);
6397         }
6398         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6399                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6400                         prog_version,
6401                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6402                         TARGET_ALIAS);
6403         }
6404
6405         /* custom headers */
6406         if (compose->account->add_customhdr) {
6407                 GSList *cur;
6408
6409                 for (cur = compose->account->customhdr_list; cur != NULL;
6410                      cur = cur->next) {
6411                         CustomHeader *chdr = (CustomHeader *)cur->data;
6412
6413                         if (custom_header_is_allowed(chdr->name)
6414                             && chdr->value != NULL
6415                             && *(chdr->value) != '\0') {
6416                                 compose_convert_header
6417                                         (compose, buf, sizeof(buf),
6418                                          chdr->value,
6419                                          strlen(chdr->name) + 2, FALSE);
6420                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6421                         }
6422                 }
6423         }
6424
6425         /* Automatic Faces and X-Faces */
6426         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6427                 g_string_append_printf(header, "X-Face: %s\n", buf);
6428         }
6429         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6430                 g_string_append_printf(header, "X-Face: %s\n", buf);
6431         }
6432         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6433                 g_string_append_printf(header, "Face: %s\n", buf);
6434         }
6435         else if (get_default_face (buf, sizeof(buf)) == 0) {
6436                 g_string_append_printf(header, "Face: %s\n", buf);
6437         }
6438
6439         /* PRIORITY */
6440         switch (compose->priority) {
6441                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6442                                                    "X-Priority: 1 (Highest)\n");
6443                         break;
6444                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6445                                                 "X-Priority: 2 (High)\n");
6446                         break;
6447                 case PRIORITY_NORMAL: break;
6448                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6449                                                "X-Priority: 4 (Low)\n");
6450                         break;
6451                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6452                                                   "X-Priority: 5 (Lowest)\n");
6453                         break;
6454                 default: debug_print("compose: priority unknown : %d\n",
6455                                      compose->priority);
6456         }
6457
6458         /* Request Return Receipt */
6459         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6460                 if (compose->return_receipt) {
6461                         if (compose->account->name
6462                             && *compose->account->name) {
6463                                 compose_convert_header(compose, buf, sizeof(buf), 
6464                                                        compose->account->name, 
6465                                                        strlen("Disposition-Notification-To: "),
6466                                                        TRUE);
6467                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6468                         } else
6469                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6470                 }
6471         }
6472
6473         /* get special headers */
6474         for (list = compose->header_list; list; list = list->next) {
6475                 ComposeHeaderEntry *headerentry;
6476                 gchar *tmp;
6477                 gchar *headername;
6478                 gchar *headername_wcolon;
6479                 const gchar *headername_trans;
6480                 gchar *headervalue;
6481                 gchar **string;
6482                 gboolean standard_header = FALSE;
6483
6484                 headerentry = ((ComposeHeaderEntry *)list->data);
6485
6486                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6487                 g_strstrip(tmp);
6488                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6489                         g_free(tmp);
6490                         continue;
6491                 }
6492
6493                 if (!strstr(tmp, ":")) {
6494                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6495                         headername = g_strdup(tmp);
6496                 } else {
6497                         headername_wcolon = g_strdup(tmp);
6498                         headername = g_strdup(strtok(tmp, ":"));
6499                 }
6500                 g_free(tmp);
6501                 
6502                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6503                 Xstrdup_a(headervalue, entry_str, return NULL);
6504                 subst_char(headervalue, '\r', ' ');
6505                 subst_char(headervalue, '\n', ' ');
6506                 string = std_headers;
6507                 while (*string != NULL) {
6508                         headername_trans = prefs_common_translated_header_name(*string);
6509                         if (!strcmp(headername_trans, headername_wcolon))
6510                                 standard_header = TRUE;
6511                         string++;
6512                 }
6513                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6514                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6515                                 
6516                 g_free(headername);
6517                 g_free(headername_wcolon);              
6518         }
6519
6520         str = header->str;
6521         g_string_free(header, FALSE);
6522
6523         return str;
6524 }
6525
6526 #undef IS_IN_CUSTOM_HEADER
6527
6528 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6529                                    gint header_len, gboolean addr_field)
6530 {
6531         gchar *tmpstr = NULL;
6532         const gchar *out_codeset = NULL;
6533
6534         cm_return_if_fail(src != NULL);
6535         cm_return_if_fail(dest != NULL);
6536
6537         if (len < 1) return;
6538
6539         tmpstr = g_strdup(src);
6540
6541         subst_char(tmpstr, '\n', ' ');
6542         subst_char(tmpstr, '\r', ' ');
6543         g_strchomp(tmpstr);
6544
6545         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6546                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6547                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6548                 g_free(tmpstr);
6549                 tmpstr = mybuf;
6550         }
6551
6552         codeconv_set_strict(TRUE);
6553         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6554                 conv_get_charset_str(compose->out_encoding));
6555         codeconv_set_strict(FALSE);
6556         
6557         if (!dest || *dest == '\0') {
6558                 gchar *test_conv_global_out = NULL;
6559                 gchar *test_conv_reply = NULL;
6560
6561                 /* automatic mode. be automatic. */
6562                 codeconv_set_strict(TRUE);
6563
6564                 out_codeset = conv_get_outgoing_charset_str();
6565                 if (out_codeset) {
6566                         debug_print("trying to convert to %s\n", out_codeset);
6567                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6568                 }
6569
6570                 if (!test_conv_global_out && compose->orig_charset
6571                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6572                         out_codeset = compose->orig_charset;
6573                         debug_print("failure; trying to convert to %s\n", out_codeset);
6574                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6575                 }
6576
6577                 if (!test_conv_global_out && !test_conv_reply) {
6578                         /* we're lost */
6579                         out_codeset = CS_INTERNAL;
6580                         debug_print("finally using %s\n", out_codeset);
6581                 }
6582                 g_free(test_conv_global_out);
6583                 g_free(test_conv_reply);
6584                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6585                                         out_codeset);
6586                 codeconv_set_strict(FALSE);
6587         }
6588         g_free(tmpstr);
6589 }
6590
6591 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6592 {
6593         gchar *address;
6594
6595         cm_return_if_fail(user_data != NULL);
6596
6597         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6598         g_strstrip(address);
6599         if (*address != '\0') {
6600                 gchar *name = procheader_get_fromname(address);
6601                 extract_address(address);
6602 #ifndef USE_NEW_ADDRBOOK
6603                 addressbook_add_contact(name, address, NULL, NULL);
6604 #else
6605                 debug_print("%s: %s\n", name, address);
6606                 if (addressadd_selection(name, address, NULL, NULL)) {
6607                         debug_print( "addressbook_add_contact - added\n" );
6608                 }
6609 #endif
6610         }
6611         g_free(address);
6612 }
6613
6614 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6615 {
6616         GtkWidget *menuitem;
6617         gchar *address;
6618
6619         cm_return_if_fail(menu != NULL);
6620         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6621
6622         menuitem = gtk_separator_menu_item_new();
6623         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6624         gtk_widget_show(menuitem);
6625
6626         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6627         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6628
6629         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6630         g_strstrip(address);
6631         if (*address == '\0') {
6632                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6633         }
6634
6635         g_signal_connect(G_OBJECT(menuitem), "activate",
6636                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6637         gtk_widget_show(menuitem);
6638 }
6639
6640 void compose_add_extra_header(gchar *header, GtkListStore *model)
6641 {
6642         GtkTreeIter iter;
6643         if (strcmp(header, "")) {
6644                 COMBOBOX_ADD(model, header, COMPOSE_TO);
6645         }
6646 }
6647
6648 void compose_add_extra_header_entries(GtkListStore *model)
6649 {
6650         FILE *exh;
6651         gchar *exhrc;
6652         gchar buf[BUFFSIZE];
6653         gint lastc;
6654
6655         if (extra_headers == NULL) {
6656                 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6657                 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6658                         debug_print("extra headers file not found\n");
6659                         goto extra_headers_done;
6660                 }
6661                 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6662                         lastc = strlen(buf) - 1;        /* remove trailing control chars */
6663                         while (lastc >= 0 && buf[lastc] != ':')
6664                                 buf[lastc--] = '\0';
6665                         if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6666                                 buf[lastc] = '\0'; /* remove trailing : for comparison */
6667                                 if (custom_header_is_allowed(buf)) {
6668                                         buf[lastc] = ':';
6669                                         extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6670                                 }
6671                                 else
6672                                         g_message("disallowed extra header line: %s\n", buf);
6673                         }
6674                         else {
6675                                 if (buf[0] != '#')
6676                                         g_message("invalid extra header line: %s\n", buf);
6677                         }
6678                 }
6679                 fclose(exh);
6680 extra_headers_done:
6681                 g_free(exhrc);
6682                 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6683                 extra_headers = g_slist_reverse(extra_headers);
6684         }
6685         g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6686 }
6687
6688 static void compose_create_header_entry(Compose *compose) 
6689 {
6690         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6691
6692         GtkWidget *combo;
6693         GtkWidget *entry;
6694         GtkWidget *button;
6695         GtkWidget *hbox;
6696         gchar **string;
6697         const gchar *header = NULL;
6698         ComposeHeaderEntry *headerentry;
6699         gboolean standard_header = FALSE;
6700         GtkListStore *model;
6701         GtkTreeIter iter;
6702 #if !(GTK_CHECK_VERSION(2,12,0))
6703         GtkTooltips *tips = compose->tooltips;
6704 #endif
6705         
6706         headerentry = g_new0(ComposeHeaderEntry, 1);
6707
6708         /* Combo box model */
6709         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6710 #if !GTK_CHECK_VERSION(2, 24, 0)
6711         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6712 #endif
6713         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6714                         COMPOSE_TO);
6715         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6716                         COMPOSE_CC);
6717         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6718                         COMPOSE_BCC);
6719         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6720                         COMPOSE_NEWSGROUPS);                    
6721         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6722                         COMPOSE_REPLYTO);
6723         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6724                         COMPOSE_FOLLOWUPTO);
6725         compose_add_extra_header_entries(model);
6726
6727         /* Combo box */
6728 #if GTK_CHECK_VERSION(2, 24, 0)
6729         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6730         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6731         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6732         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6733         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6734 #endif
6735         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6736         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6737                          G_CALLBACK(compose_grab_focus_cb), compose);
6738         gtk_widget_show(combo);
6739         
6740         GList *l = NULL;
6741         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6742         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6743         g_list_free(l);
6744
6745         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6746                         compose->header_nextrow, compose->header_nextrow+1,
6747                         GTK_SHRINK, GTK_FILL, 0, 0);
6748         if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6749                 const gchar *last_header_entry = gtk_entry_get_text(
6750                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6751                 string = headers;
6752                 while (*string != NULL) {
6753                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6754                                 standard_header = TRUE;
6755                         string++;
6756                 }
6757                 if (standard_header)
6758                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6759         }
6760         if (!compose->header_last || !standard_header) {
6761                 switch(compose->account->protocol) {
6762                         case A_NNTP:
6763                                 header = prefs_common_translated_header_name("Newsgroups:");
6764                                 break;
6765                         default:
6766                                 header = prefs_common_translated_header_name("To:");
6767                                 break;
6768                 }                                                                   
6769         }
6770         if (header)
6771                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6772
6773         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6774                          G_CALLBACK(compose_grab_focus_cb), compose);
6775
6776         /* Entry field with cleanup button */
6777         button = gtk_button_new();
6778         gtk_button_set_image(GTK_BUTTON(button),
6779                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6780         gtk_widget_show(button);
6781         CLAWS_SET_TIP(button,
6782                 _("Delete entry contents"));
6783         entry = gtk_entry_new(); 
6784         gtk_widget_show(entry);
6785         CLAWS_SET_TIP(entry,
6786                 _("Use <tab> to autocomplete from addressbook"));
6787         hbox = gtk_hbox_new (FALSE, 0);
6788         gtk_widget_show(hbox);
6789         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6790         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6791         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6792                         compose->header_nextrow, compose->header_nextrow+1,
6793                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6794
6795         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6796                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6797                          headerentry);
6798         g_signal_connect(G_OBJECT(entry), "changed", 
6799                          G_CALLBACK(compose_headerentry_changed_cb), 
6800                          headerentry);
6801         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6802                          G_CALLBACK(compose_grab_focus_cb), compose);
6803
6804         g_signal_connect(G_OBJECT(button), "clicked",
6805                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6806                          headerentry); 
6807                          
6808         /* email dnd */
6809         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6810                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6811                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6812         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6813                          G_CALLBACK(compose_header_drag_received_cb),
6814                          entry);
6815         g_signal_connect(G_OBJECT(entry), "drag-drop",
6816                          G_CALLBACK(compose_drag_drop),
6817                          compose);
6818         g_signal_connect(G_OBJECT(entry), "populate-popup",
6819                          G_CALLBACK(compose_entry_popup_extend),
6820                          NULL);
6821         
6822         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6823
6824         headerentry->compose = compose;
6825         headerentry->combo = combo;
6826         headerentry->entry = entry;
6827         headerentry->button = button;
6828         headerentry->hbox = hbox;
6829         headerentry->headernum = compose->header_nextrow;
6830         headerentry->type = PREF_NONE;
6831
6832         compose->header_nextrow++;
6833         compose->header_last = headerentry;             
6834         compose->header_list =
6835                 g_slist_append(compose->header_list,
6836                                headerentry);
6837 }
6838
6839 static void compose_add_header_entry(Compose *compose, const gchar *header,
6840                                 gchar *text, ComposePrefType pref_type) 
6841 {
6842         ComposeHeaderEntry *last_header = compose->header_last;
6843         gchar *tmp = g_strdup(text), *email;
6844         gboolean replyto_hdr;
6845         
6846         replyto_hdr = (!strcasecmp(header,
6847                                 prefs_common_translated_header_name("Reply-To:")) ||
6848                         !strcasecmp(header,
6849                                 prefs_common_translated_header_name("Followup-To:")) ||
6850                         !strcasecmp(header,
6851                                 prefs_common_translated_header_name("In-Reply-To:")));
6852                 
6853         extract_address(tmp);
6854         email = g_utf8_strdown(tmp, -1);
6855         
6856         if (replyto_hdr == FALSE &&
6857             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6858         {
6859                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6860                                 header, text, (gint) pref_type);
6861                 g_free(email);
6862                 g_free(tmp);
6863                 return;
6864         }
6865         
6866         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6867                 gtk_entry_set_text(GTK_ENTRY(
6868                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6869         else
6870                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6871         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6872         last_header->type = pref_type;
6873
6874         if (replyto_hdr == FALSE)
6875                 g_hash_table_insert(compose->email_hashtable, email,
6876                                     GUINT_TO_POINTER(1));
6877         else
6878                 g_free(email);
6879         
6880         g_free(tmp);
6881 }
6882
6883 static void compose_destroy_headerentry(Compose *compose, 
6884                                         ComposeHeaderEntry *headerentry)
6885 {
6886         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6887         gchar *email;
6888
6889         extract_address(text);
6890         email = g_utf8_strdown(text, -1);
6891         g_hash_table_remove(compose->email_hashtable, email);
6892         g_free(text);
6893         g_free(email);
6894         
6895         gtk_widget_destroy(headerentry->combo);
6896         gtk_widget_destroy(headerentry->entry);
6897         gtk_widget_destroy(headerentry->button);
6898         gtk_widget_destroy(headerentry->hbox);
6899         g_free(headerentry);
6900 }
6901
6902 static void compose_remove_header_entries(Compose *compose) 
6903 {
6904         GSList *list;
6905         for (list = compose->header_list; list; list = list->next)
6906                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6907
6908         compose->header_last = NULL;
6909         g_slist_free(compose->header_list);
6910         compose->header_list = NULL;
6911         compose->header_nextrow = 1;
6912         compose_create_header_entry(compose);
6913 }
6914
6915 static GtkWidget *compose_create_header(Compose *compose) 
6916 {
6917         GtkWidget *from_optmenu_hbox;
6918         GtkWidget *header_scrolledwin_main;
6919         GtkWidget *header_table_main;
6920         GtkWidget *header_scrolledwin;
6921         GtkWidget *header_table;
6922
6923         /* parent with account selection and from header */
6924         header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6925         gtk_widget_show(header_scrolledwin_main);
6926         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6927
6928         header_table_main = gtk_table_new(2, 2, FALSE);
6929         gtk_widget_show(header_table_main);
6930         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6931         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6932         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6933
6934         from_optmenu_hbox = compose_account_option_menu_create(compose);
6935         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6936                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6937
6938         /* child with header labels and entries */
6939         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6940         gtk_widget_show(header_scrolledwin);
6941         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6942
6943         header_table = gtk_table_new(2, 2, FALSE);
6944         gtk_widget_show(header_table);
6945         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6946         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6947         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6948
6949         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6950                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6951
6952         compose->header_table = header_table;
6953         compose->header_list = NULL;
6954         compose->header_nextrow = 0;
6955
6956         compose_create_header_entry(compose);
6957
6958         compose->table = NULL;
6959
6960         return header_scrolledwin_main;
6961 }
6962
6963 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6964 {
6965         Compose *compose = (Compose *)data;
6966         GdkEventButton event;
6967         
6968         event.button = 3;
6969         event.time = gtk_get_current_event_time();
6970
6971         return attach_button_pressed(compose->attach_clist, &event, compose);
6972 }
6973
6974 static GtkWidget *compose_create_attach(Compose *compose)
6975 {
6976         GtkWidget *attach_scrwin;
6977         GtkWidget *attach_clist;
6978
6979         GtkListStore *store;
6980         GtkCellRenderer *renderer;
6981         GtkTreeViewColumn *column;
6982         GtkTreeSelection *selection;
6983
6984         /* attachment list */
6985         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6986         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6987                                        GTK_POLICY_AUTOMATIC,
6988                                        GTK_POLICY_AUTOMATIC);
6989         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6990
6991         store = gtk_list_store_new(N_ATTACH_COLS, 
6992                                    G_TYPE_STRING,
6993                                    G_TYPE_STRING,
6994                                    G_TYPE_STRING,
6995                                    G_TYPE_STRING,
6996                                    G_TYPE_POINTER,
6997                                    G_TYPE_AUTO_POINTER,
6998                                    -1);
6999         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7000                                         (GTK_TREE_MODEL(store)));
7001         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7002         g_object_unref(store);
7003         
7004         renderer = gtk_cell_renderer_text_new();
7005         column = gtk_tree_view_column_new_with_attributes
7006                         (_("Mime type"), renderer, "text", 
7007                          COL_MIMETYPE, NULL);
7008         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7009         
7010         renderer = gtk_cell_renderer_text_new();
7011         column = gtk_tree_view_column_new_with_attributes
7012                         (_("Size"), renderer, "text", 
7013                          COL_SIZE, NULL);
7014         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7015         
7016         renderer = gtk_cell_renderer_text_new();
7017         column = gtk_tree_view_column_new_with_attributes
7018                         (_("Name"), renderer, "text", 
7019                          COL_NAME, NULL);
7020         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7021
7022         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7023                                      prefs_common.use_stripes_everywhere);
7024         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7025         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7026
7027         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7028                          G_CALLBACK(attach_selected), compose);
7029         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7030                          G_CALLBACK(attach_button_pressed), compose);
7031         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7032                          G_CALLBACK(popup_attach_button_pressed), compose);
7033         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7034                          G_CALLBACK(attach_key_pressed), compose);
7035
7036         /* drag and drop */
7037         gtk_drag_dest_set(attach_clist,
7038                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7039                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7040                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7041         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7042                          G_CALLBACK(compose_attach_drag_received_cb),
7043                          compose);
7044         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7045                          G_CALLBACK(compose_drag_drop),
7046                          compose);
7047
7048         compose->attach_scrwin = attach_scrwin;
7049         compose->attach_clist  = attach_clist;
7050
7051         return attach_scrwin;
7052 }
7053
7054 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7055 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7056
7057 static GtkWidget *compose_create_others(Compose *compose)
7058 {
7059         GtkWidget *table;
7060         GtkWidget *savemsg_checkbtn;
7061         GtkWidget *savemsg_combo;
7062         GtkWidget *savemsg_select;
7063         
7064         guint rowcount = 0;
7065         gchar *folderidentifier;
7066
7067         /* Table for settings */
7068         table = gtk_table_new(3, 1, FALSE);
7069         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7070         gtk_widget_show(table);
7071         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7072         rowcount = 0;
7073
7074         /* Save Message to folder */
7075         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7076         gtk_widget_show(savemsg_checkbtn);
7077         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7078         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7079                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7080         }
7081         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7082                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7083
7084 #if !GTK_CHECK_VERSION(2, 24, 0)
7085         savemsg_combo = gtk_combo_box_entry_new_text();
7086 #else
7087         savemsg_combo = gtk_combo_box_text_new_with_entry();
7088 #endif
7089         compose->savemsg_checkbtn = savemsg_checkbtn;
7090         compose->savemsg_combo = savemsg_combo;
7091         gtk_widget_show(savemsg_combo);
7092
7093         if (prefs_common.compose_save_to_history)
7094 #if !GTK_CHECK_VERSION(2, 24, 0)
7095                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7096                                 prefs_common.compose_save_to_history);
7097 #else
7098                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7099                                 prefs_common.compose_save_to_history);
7100 #endif
7101         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7102         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7103         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7104                          G_CALLBACK(compose_grab_focus_cb), compose);
7105         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7106                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7107                                   (compose->account, F_OUTBOX));
7108                 compose_set_save_to(compose, folderidentifier);
7109                 g_free(folderidentifier);
7110         }
7111
7112         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7113         gtk_widget_show(savemsg_select);
7114         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7115         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7116                          G_CALLBACK(compose_savemsg_select_cb),
7117                          compose);
7118
7119         return table;   
7120 }
7121
7122 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7123 {
7124         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7125                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7126 }
7127
7128 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7129 {
7130         FolderItem *dest;
7131         gchar * path;
7132
7133         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7134         if (!dest) return;
7135
7136         path = folder_item_get_identifier(dest);
7137
7138         compose_set_save_to(compose, path);
7139         g_free(path);
7140 }
7141
7142 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7143                                   GdkAtom clip, GtkTextIter *insert_place);
7144
7145
7146 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7147                                        Compose *compose)
7148 {
7149         gint prev_autowrap;
7150         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7151 #if USE_ENCHANT
7152         if (event->button == 3) {
7153                 GtkTextIter iter;
7154                 GtkTextIter sel_start, sel_end;
7155                 gboolean stuff_selected;
7156                 gint x, y;
7157                 /* move the cursor to allow GtkAspell to check the word
7158                  * under the mouse */
7159                 if (event->x && event->y) {
7160                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7161                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7162                                 &x, &y);
7163                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7164                                 &iter, x, y);
7165                 } else {
7166                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7167                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7168                 }
7169                 /* get selection */
7170                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7171                                 buffer,
7172                                 &sel_start, &sel_end);
7173
7174                 gtk_text_buffer_place_cursor (buffer, &iter);
7175                 /* reselect stuff */
7176                 if (stuff_selected 
7177                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7178                         gtk_text_buffer_select_range(buffer,
7179                                 &sel_start, &sel_end);
7180                 }
7181                 return FALSE; /* pass the event so that the right-click goes through */
7182         }
7183 #endif
7184         if (event->button == 2) {
7185                 GtkTextIter iter;
7186                 gint x, y;
7187                 BLOCK_WRAP();
7188                 
7189                 /* get the middle-click position to paste at the correct place */
7190                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7191                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7192                         &x, &y);
7193                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7194                         &iter, x, y);
7195                 
7196                 entry_paste_clipboard(compose, text, 
7197                                 prefs_common.linewrap_pastes,
7198                                 GDK_SELECTION_PRIMARY, &iter);
7199                 UNBLOCK_WRAP();
7200                 return TRUE;
7201         }
7202         return FALSE;
7203 }
7204
7205 #if USE_ENCHANT
7206 static void compose_spell_menu_changed(void *data)
7207 {
7208         Compose *compose = (Compose *)data;
7209         GSList *items;
7210         GtkWidget *menuitem;
7211         GtkWidget *parent_item;
7212         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7213         GSList *spell_menu;
7214
7215         if (compose->gtkaspell == NULL)
7216                 return;
7217
7218         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7219                         "/Menu/Spelling/Options");
7220
7221         /* setting the submenu removes /Spelling/Options from the factory 
7222          * so we need to save it */
7223
7224         if (parent_item == NULL) {
7225                 parent_item = compose->aspell_options_menu;
7226                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7227         } else
7228                 compose->aspell_options_menu = parent_item;
7229
7230         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7231
7232         spell_menu = g_slist_reverse(spell_menu);
7233         for (items = spell_menu;
7234              items; items = items->next) {
7235                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7236                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7237                 gtk_widget_show(GTK_WIDGET(menuitem));
7238         }
7239         g_slist_free(spell_menu);
7240
7241         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7242         gtk_widget_show(parent_item);
7243 }
7244
7245 static void compose_dict_changed(void *data)
7246 {
7247         Compose *compose = (Compose *) data;
7248
7249         if(compose->gtkaspell && 
7250            compose->gtkaspell->recheck_when_changing_dict == FALSE)
7251                 return;
7252
7253         gtkaspell_highlight_all(compose->gtkaspell);
7254         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7255 }
7256 #endif
7257
7258 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7259 {
7260         Compose *compose = (Compose *)data;
7261         GdkEventButton event;
7262         
7263         event.button = 3;
7264         event.time = gtk_get_current_event_time();
7265         event.x = 0;
7266         event.y = 0;
7267
7268         return text_clicked(compose->text, &event, compose);
7269 }
7270
7271 static gboolean compose_force_window_origin = TRUE;
7272 static Compose *compose_create(PrefsAccount *account,
7273                                                  FolderItem *folder,
7274                                                  ComposeMode mode,
7275                                                  gboolean batch)
7276 {
7277         Compose   *compose;
7278         GtkWidget *window;
7279         GtkWidget *vbox;
7280         GtkWidget *menubar;
7281         GtkWidget *handlebox;
7282
7283         GtkWidget *notebook;
7284         
7285         GtkWidget *attach_hbox;
7286         GtkWidget *attach_lab1;
7287         GtkWidget *attach_lab2;
7288
7289         GtkWidget *vbox2;
7290
7291         GtkWidget *label;
7292         GtkWidget *subject_hbox;
7293         GtkWidget *subject_frame;
7294         GtkWidget *subject_entry;
7295         GtkWidget *subject;
7296         GtkWidget *paned;
7297
7298         GtkWidget *edit_vbox;
7299         GtkWidget *ruler_hbox;
7300         GtkWidget *ruler;
7301         GtkWidget *scrolledwin;
7302         GtkWidget *text;
7303         GtkTextBuffer *buffer;
7304         GtkClipboard *clipboard;
7305
7306         UndoMain *undostruct;
7307
7308         GtkWidget *popupmenu;
7309         GtkWidget *tmpl_menu;
7310         GtkActionGroup *action_group = NULL;
7311
7312 #if USE_ENCHANT
7313         GtkAspell * gtkaspell = NULL;
7314 #endif
7315
7316         static GdkGeometry geometry;
7317
7318         cm_return_val_if_fail(account != NULL, NULL);
7319
7320         debug_print("Creating compose window...\n");
7321         compose = g_new0(Compose, 1);
7322
7323         compose->batch = batch;
7324         compose->account = account;
7325         compose->folder = folder;
7326         
7327         compose->mutex = cm_mutex_new();
7328         compose->set_cursor_pos = -1;
7329
7330 #if !(GTK_CHECK_VERSION(2,12,0))
7331         compose->tooltips = tips;
7332 #endif
7333
7334         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7335
7336         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7337         gtk_widget_set_size_request(window, prefs_common.compose_width,
7338                                         prefs_common.compose_height);
7339
7340         if (!geometry.max_width) {
7341                 geometry.max_width = gdk_screen_width();
7342                 geometry.max_height = gdk_screen_height();
7343         }
7344
7345         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7346                                       &geometry, GDK_HINT_MAX_SIZE);
7347         if (!geometry.min_width) {
7348                 geometry.min_width = 600;
7349                 geometry.min_height = 440;
7350         }
7351         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7352                                       &geometry, GDK_HINT_MIN_SIZE);
7353
7354 #ifndef GENERIC_UMPC    
7355         if (compose_force_window_origin)
7356                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7357                                  prefs_common.compose_y);
7358 #endif
7359         g_signal_connect(G_OBJECT(window), "delete_event",
7360                          G_CALLBACK(compose_delete_cb), compose);
7361         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7362         gtk_widget_realize(window);
7363
7364         gtkut_widget_set_composer_icon(window);
7365
7366         vbox = gtk_vbox_new(FALSE, 0);
7367         gtk_container_add(GTK_CONTAINER(window), vbox);
7368
7369         compose->ui_manager = gtk_ui_manager_new();
7370         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7371                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7372         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7373                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7374         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7375                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7376         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7377                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7378         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7379                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7380
7381         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7382
7383         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7384         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7385 #ifdef USE_ENCHANT
7386         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7387 #endif
7388         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7389         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7390         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7391
7392 /* Compose menu */
7393         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7394         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7395         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7396         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7397         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7398         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7399         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7400         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7401         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7402         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7403         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7404         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7405
7406 /* Edit menu */
7407         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7408         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7409         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7410
7411         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7412         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7413         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7414
7415         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7416         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7417         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7418         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7419
7420         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7421
7422         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7423         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7424         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7425         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7426         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7427         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7428         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7429         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7430         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7431         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7432         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7433         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7434         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7435         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7436         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7437
7438         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7439
7440         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7441         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7442         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7443         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7444         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7445
7446         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7447
7448         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7449
7450 #if USE_ENCHANT
7451 /* Spelling menu */
7452         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7453         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7454         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7455         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7456         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7457         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7458 #endif
7459
7460 /* Options menu */
7461         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7462         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7463         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7464         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7465         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7466
7467         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7468         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7469         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7470         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7471         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7472
7473         
7474         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7475         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7476         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7477         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7478         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7479         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7480         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7481
7482         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7483         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7484         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7485         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7486         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7487
7488         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7489
7490         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7491         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7492         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7493         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7494         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7495
7496         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7497         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)
7498         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)
7499         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7500
7501         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7502
7503         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7504         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)
7505         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)
7506
7507         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7508
7509         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7510         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)
7511         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7512
7513         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7514         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)
7515         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7516
7517         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7518
7519         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7520         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)
7521         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7522         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7523         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7524
7525         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7526         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)
7527         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)
7528         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7529         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7530
7531         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7532         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7533         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7534         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7535         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7536         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7537
7538         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7539         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7540         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)
7541
7542         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7543         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7544         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7545 /* phew. */
7546
7547 /* Tools menu */
7548         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7549         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7550         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7551         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7552         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7553         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7554
7555 /* Help menu */
7556         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7557
7558         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7559         gtk_widget_show_all(menubar);
7560
7561         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7562         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7563
7564         if (prefs_common.toolbar_detachable) {
7565                 handlebox = gtk_handle_box_new();
7566         } else {
7567                 handlebox = gtk_hbox_new(FALSE, 0);
7568         }
7569         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7570
7571         gtk_widget_realize(handlebox);
7572         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7573                                           (gpointer)compose);
7574
7575         vbox2 = gtk_vbox_new(FALSE, 2);
7576         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7577         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7578         
7579         /* Notebook */
7580         notebook = gtk_notebook_new();
7581         gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7582         gtk_widget_show(notebook);
7583
7584         /* header labels and entries */
7585         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7586                         compose_create_header(compose),
7587                         gtk_label_new_with_mnemonic(_("Hea_der")));
7588         /* attachment list */
7589         attach_hbox = gtk_hbox_new(FALSE, 0);
7590         gtk_widget_show(attach_hbox);
7591         
7592         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7593         gtk_widget_show(attach_lab1);
7594         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7595         
7596         attach_lab2 = gtk_label_new("");
7597         gtk_widget_show(attach_lab2);
7598         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7599         
7600         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7601                         compose_create_attach(compose),
7602                         attach_hbox);
7603         /* Others Tab */
7604         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7605                         compose_create_others(compose),
7606                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7607
7608         /* Subject */
7609         subject_hbox = gtk_hbox_new(FALSE, 0);
7610         gtk_widget_show(subject_hbox);
7611
7612         subject_frame = gtk_frame_new(NULL);
7613         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7614         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7615         gtk_widget_show(subject_frame);
7616
7617         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7618         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7619         gtk_widget_show(subject);
7620
7621         label = gtk_label_new(_("Subject:"));
7622         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7623         gtk_widget_show(label);
7624
7625 #ifdef USE_ENCHANT
7626         subject_entry = claws_spell_entry_new();
7627 #else
7628         subject_entry = gtk_entry_new();
7629 #endif
7630         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7631         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7632                          G_CALLBACK(compose_grab_focus_cb), compose);
7633         gtk_widget_show(subject_entry);
7634         compose->subject_entry = subject_entry;
7635         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7636         
7637         edit_vbox = gtk_vbox_new(FALSE, 0);
7638
7639         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7640
7641         /* ruler */
7642         ruler_hbox = gtk_hbox_new(FALSE, 0);
7643         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7644
7645         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7646         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7647         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7648                            BORDER_WIDTH);
7649
7650         /* text widget */
7651         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7652         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7653                                        GTK_POLICY_AUTOMATIC,
7654                                        GTK_POLICY_AUTOMATIC);
7655         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7656                                             GTK_SHADOW_IN);
7657         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7658
7659         text = gtk_text_view_new();
7660         if (prefs_common.show_compose_margin) {
7661                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7662                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7663         }
7664         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7665         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7666         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7667         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7668         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7669         
7670         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7671         g_signal_connect(G_OBJECT(notebook), "size_allocate",
7672                          G_CALLBACK(compose_notebook_size_alloc), compose);     
7673         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7674                                G_CALLBACK(compose_edit_size_alloc),
7675                                ruler);
7676         g_signal_connect(G_OBJECT(buffer), "changed",
7677                          G_CALLBACK(compose_changed_cb), compose);
7678         g_signal_connect(G_OBJECT(text), "grab_focus",
7679                          G_CALLBACK(compose_grab_focus_cb), compose);
7680         g_signal_connect(G_OBJECT(buffer), "insert_text",
7681                          G_CALLBACK(text_inserted), compose);
7682         g_signal_connect(G_OBJECT(text), "button_press_event",
7683                          G_CALLBACK(text_clicked), compose);
7684         g_signal_connect(G_OBJECT(text), "popup-menu",
7685                          G_CALLBACK(compose_popup_menu), compose);
7686         g_signal_connect(G_OBJECT(subject_entry), "changed",
7687                         G_CALLBACK(compose_changed_cb), compose);
7688         g_signal_connect(G_OBJECT(subject_entry), "activate",
7689                         G_CALLBACK(compose_subject_entry_activated), compose);
7690
7691         /* drag and drop */
7692         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7693                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7694                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7695         g_signal_connect(G_OBJECT(text), "drag_data_received",
7696                          G_CALLBACK(compose_insert_drag_received_cb),
7697                          compose);
7698         g_signal_connect(G_OBJECT(text), "drag-drop",
7699                          G_CALLBACK(compose_drag_drop),
7700                          compose);
7701         g_signal_connect(G_OBJECT(text), "key-press-event",
7702                          G_CALLBACK(completion_set_focus_to_subject),
7703                          compose);
7704         gtk_widget_show_all(vbox);
7705
7706         /* pane between attach clist and text */
7707         paned = gtk_vpaned_new();
7708         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7709         gtk_paned_add1(GTK_PANED(paned), notebook);
7710         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7711         gtk_widget_show_all(paned);
7712
7713
7714         if (prefs_common.textfont) {
7715                 PangoFontDescription *font_desc;
7716
7717                 font_desc = pango_font_description_from_string
7718                         (prefs_common.textfont);
7719                 if (font_desc) {
7720                         gtk_widget_modify_font(text, font_desc);
7721                         pango_font_description_free(font_desc);
7722                 }
7723         }
7724
7725         gtk_action_group_add_actions(action_group, compose_popup_entries,
7726                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7727         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7728         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7729         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7730         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7731         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7732         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7733         
7734         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7735
7736         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7737         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7738         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7739
7740         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7741
7742         undostruct = undo_init(text);
7743         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7744                                    compose);
7745
7746         address_completion_start(window);
7747
7748         compose->window        = window;
7749         compose->vbox          = vbox;
7750         compose->menubar       = menubar;
7751         compose->handlebox     = handlebox;
7752
7753         compose->vbox2         = vbox2;
7754
7755         compose->paned = paned;
7756
7757         compose->attach_label  = attach_lab2;
7758
7759         compose->notebook      = notebook;
7760         compose->edit_vbox     = edit_vbox;
7761         compose->ruler_hbox    = ruler_hbox;
7762         compose->ruler         = ruler;
7763         compose->scrolledwin   = scrolledwin;
7764         compose->text          = text;
7765
7766         compose->focused_editable = NULL;
7767
7768         compose->popupmenu    = popupmenu;
7769
7770         compose->tmpl_menu = tmpl_menu;
7771
7772         compose->mode = mode;
7773         compose->rmode = mode;
7774
7775         compose->targetinfo = NULL;
7776         compose->replyinfo  = NULL;
7777         compose->fwdinfo    = NULL;
7778
7779         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7780                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7781         
7782         compose->replyto     = NULL;
7783         compose->cc          = NULL;
7784         compose->bcc         = NULL;
7785         compose->followup_to = NULL;
7786
7787         compose->ml_post     = NULL;
7788
7789         compose->inreplyto   = NULL;
7790         compose->references  = NULL;
7791         compose->msgid       = NULL;
7792         compose->boundary    = NULL;
7793
7794         compose->autowrap       = prefs_common.autowrap;
7795         compose->autoindent     = prefs_common.auto_indent;
7796         compose->use_signing    = FALSE;
7797         compose->use_encryption = FALSE;
7798         compose->privacy_system = NULL;
7799
7800         compose->modified = FALSE;
7801
7802         compose->return_receipt = FALSE;
7803
7804         compose->to_list        = NULL;
7805         compose->newsgroup_list = NULL;
7806
7807         compose->undostruct = undostruct;
7808
7809         compose->sig_str = NULL;
7810
7811         compose->exteditor_file    = NULL;
7812         compose->exteditor_pid     = -1;
7813         compose->exteditor_tag     = -1;
7814         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7815
7816 #if USE_ENCHANT
7817         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7818         if (mode != COMPOSE_REDIRECT) {
7819                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7820                     strcmp(prefs_common.dictionary, "")) {
7821                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7822                                                   prefs_common.alt_dictionary,
7823                                                   conv_get_locale_charset_str(),
7824                                                   prefs_common.misspelled_col,
7825                                                   prefs_common.check_while_typing,
7826                                                   prefs_common.recheck_when_changing_dict,
7827                                                   prefs_common.use_alternate,
7828                                                   prefs_common.use_both_dicts,
7829                                                   GTK_TEXT_VIEW(text),
7830                                                   GTK_WINDOW(compose->window),
7831                                                   compose_dict_changed,
7832                                                   compose_spell_menu_changed,
7833                                                   compose);
7834                         if (!gtkaspell) {
7835                                 alertpanel_error(_("Spell checker could not "
7836                                                 "be started.\n%s"),
7837                                                 gtkaspell_checkers_strerror());
7838                                 gtkaspell_checkers_reset_error();
7839                         } else {
7840                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7841                         }
7842                 }
7843         }
7844         compose->gtkaspell = gtkaspell;
7845         compose_spell_menu_changed(compose);
7846         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7847 #endif
7848
7849         compose_select_account(compose, account, TRUE);
7850
7851         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7852         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7853
7854         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7855                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7856
7857         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7858                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7859         
7860         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7861                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7862
7863         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7864         if (account->protocol != A_NNTP)
7865                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7866                                 prefs_common_translated_header_name("To:"));
7867         else
7868                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7869                                 prefs_common_translated_header_name("Newsgroups:"));
7870
7871 #ifndef USE_NEW_ADDRBOOK
7872         addressbook_set_target_compose(compose);
7873 #endif  
7874         if (mode != COMPOSE_REDIRECT)
7875                 compose_set_template_menu(compose);
7876         else {
7877                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7878         }
7879
7880         compose_list = g_list_append(compose_list, compose);
7881
7882         if (!prefs_common.show_ruler)
7883                 gtk_widget_hide(ruler_hbox);
7884                 
7885         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7886
7887         /* Priority */
7888         compose->priority = PRIORITY_NORMAL;
7889         compose_update_priority_menu_item(compose);
7890
7891         compose_set_out_encoding(compose);
7892         
7893         /* Actions menu */
7894         compose_update_actions_menu(compose);
7895
7896         /* Privacy Systems menu */
7897         compose_update_privacy_systems_menu(compose);
7898
7899         activate_privacy_system(compose, account, TRUE);
7900         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7901         if (batch) {
7902                 gtk_widget_realize(window);
7903         } else {
7904                 gtk_widget_show(window);
7905         }
7906         
7907         return compose;
7908 }
7909
7910 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7911 {
7912         GList *accounts;
7913         GtkWidget *hbox;
7914         GtkWidget *optmenu;
7915         GtkWidget *optmenubox;
7916         GtkListStore *menu;
7917         GtkTreeIter iter;
7918         GtkWidget *from_name = NULL;
7919 #if !(GTK_CHECK_VERSION(2,12,0))
7920         GtkTooltips *tips = compose->tooltips;
7921 #endif
7922
7923         gint num = 0, def_menu = 0;
7924         
7925         accounts = account_get_list();
7926         cm_return_val_if_fail(accounts != NULL, NULL);
7927
7928         optmenubox = gtk_event_box_new();
7929         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7930         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7931
7932         hbox = gtk_hbox_new(FALSE, 6);
7933         from_name = gtk_entry_new();
7934         
7935         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7936                          G_CALLBACK(compose_grab_focus_cb), compose);
7937
7938         for (; accounts != NULL; accounts = accounts->next, num++) {
7939                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7940                 gchar *name, *from = NULL;
7941
7942                 if (ac == compose->account) def_menu = num;
7943
7944                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7945                                        ac->account_name);
7946                 
7947                 if (ac == compose->account) {
7948                         if (ac->name && *ac->name) {
7949                                 gchar *buf;
7950                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7951                                 from = g_strdup_printf("%s <%s>",
7952                                                        buf, ac->address);
7953                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7954                         } else {
7955                                 from = g_strdup_printf("%s",
7956                                                        ac->address);
7957                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7958                         }
7959                 }
7960                 COMBOBOX_ADD(menu, name, ac->account_id);
7961                 g_free(name);
7962                 g_free(from);
7963         }
7964
7965         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7966
7967         g_signal_connect(G_OBJECT(optmenu), "changed",
7968                         G_CALLBACK(account_activated),
7969                         compose);
7970         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7971                          G_CALLBACK(compose_entry_popup_extend),
7972                          NULL);
7973
7974         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7975         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7976         
7977         CLAWS_SET_TIP(optmenubox,
7978                 _("Account to use for this email"));
7979         CLAWS_SET_TIP(from_name,
7980                 _("Sender address to be used"));
7981
7982         compose->account_combo = optmenu;
7983         compose->from_name = from_name;
7984         
7985         return hbox;
7986 }
7987
7988 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7989 {
7990         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7991         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7992         Compose *compose = (Compose *) data;
7993         if (active) {
7994                 compose->priority = value;
7995         }
7996 }
7997
7998 static void compose_reply_change_mode(Compose *compose,
7999                                     ComposeMode action)
8000 {
8001         gboolean was_modified = compose->modified;
8002
8003         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8004         
8005         cm_return_if_fail(compose->replyinfo != NULL);
8006         
8007         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8008                 ml = TRUE;
8009         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8010                 followup = TRUE;
8011         if (action == COMPOSE_REPLY_TO_ALL)
8012                 all = TRUE;
8013         if (action == COMPOSE_REPLY_TO_SENDER)
8014                 sender = TRUE;
8015         if (action == COMPOSE_REPLY_TO_LIST)
8016                 ml = TRUE;
8017
8018         compose_remove_header_entries(compose);
8019         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8020         if (compose->account->set_autocc && compose->account->auto_cc)
8021                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8022
8023         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8024                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8025         
8026         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8027                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8028         compose_show_first_last_header(compose, TRUE);
8029         compose->modified = was_modified;
8030         compose_set_title(compose);
8031 }
8032
8033 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8034 {
8035         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8036         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8037         Compose *compose = (Compose *) data;
8038         
8039         if (active)
8040                 compose_reply_change_mode(compose, value);
8041 }
8042
8043 static void compose_update_priority_menu_item(Compose * compose)
8044 {
8045         GtkWidget *menuitem = NULL;
8046         switch (compose->priority) {
8047                 case PRIORITY_HIGHEST:
8048                         menuitem = gtk_ui_manager_get_widget
8049                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8050                         break;
8051                 case PRIORITY_HIGH:
8052                         menuitem = gtk_ui_manager_get_widget
8053                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8054                         break;
8055                 case PRIORITY_NORMAL:
8056                         menuitem = gtk_ui_manager_get_widget
8057                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8058                         break;
8059                 case PRIORITY_LOW:
8060                         menuitem = gtk_ui_manager_get_widget
8061                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8062                         break;
8063                 case PRIORITY_LOWEST:
8064                         menuitem = gtk_ui_manager_get_widget
8065                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8066                         break;
8067         }
8068         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8069 }       
8070
8071 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8072 {
8073         Compose *compose = (Compose *) data;
8074         gchar *systemid;
8075         gboolean can_sign = FALSE, can_encrypt = FALSE;
8076
8077         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8078
8079         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8080                 return;
8081
8082         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8083         g_free(compose->privacy_system);
8084         compose->privacy_system = NULL;
8085         if (systemid != NULL) {
8086                 compose->privacy_system = g_strdup(systemid);
8087
8088                 can_sign = privacy_system_can_sign(systemid);
8089                 can_encrypt = privacy_system_can_encrypt(systemid);
8090         }
8091
8092         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8093
8094         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8095         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8096 }
8097
8098 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8099 {
8100         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8101         GtkWidget *menuitem = NULL;
8102         GList *children, *amenu;
8103         gboolean can_sign = FALSE, can_encrypt = FALSE;
8104         gboolean found = FALSE;
8105
8106         if (compose->privacy_system != NULL) {
8107                 gchar *systemid;
8108                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8109                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8110                 cm_return_if_fail(menuitem != NULL);
8111
8112                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8113                 amenu = children;
8114                 menuitem = NULL;
8115                 while (amenu != NULL) {
8116                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8117                         if (systemid != NULL) {
8118                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8119                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8120                                         menuitem = GTK_WIDGET(amenu->data);
8121
8122                                         can_sign = privacy_system_can_sign(systemid);
8123                                         can_encrypt = privacy_system_can_encrypt(systemid);
8124                                         found = TRUE;
8125                                         break;
8126                                 } 
8127                         } else if (strlen(compose->privacy_system) == 0 && 
8128                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8129                                         menuitem = GTK_WIDGET(amenu->data);
8130
8131                                         can_sign = FALSE;
8132                                         can_encrypt = FALSE;
8133                                         found = TRUE;
8134                                         break;
8135                         }
8136
8137                         amenu = amenu->next;
8138                 }
8139                 g_list_free(children);
8140                 if (menuitem != NULL)
8141                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8142                 
8143                 if (warn && !found && strlen(compose->privacy_system)) {
8144                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8145                                   "will not be able to sign or encrypt this message."),
8146                                   compose->privacy_system);
8147                 }
8148         } 
8149
8150         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8151         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8152 }       
8153  
8154 static void compose_set_out_encoding(Compose *compose)
8155 {
8156         CharSet out_encoding;
8157         const gchar *branch = NULL;
8158         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8159
8160         switch(out_encoding) {
8161                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8162                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8163                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8164                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8165                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8166                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8167                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8168                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8169                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8170                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8171                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8172                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8173                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8174                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8175                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8176                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8177                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8178                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8179                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8180                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8181                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8182                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8183                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8184                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8185                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8186                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8187                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8188                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8189                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8190                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8191                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8192                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8193                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8194         }
8195         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8196 }
8197
8198 static void compose_set_template_menu(Compose *compose)
8199 {
8200         GSList *tmpl_list, *cur;
8201         GtkWidget *menu;
8202         GtkWidget *item;
8203
8204         tmpl_list = template_get_config();
8205
8206         menu = gtk_menu_new();
8207
8208         gtk_menu_set_accel_group (GTK_MENU (menu), 
8209                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8210         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8211                 Template *tmpl = (Template *)cur->data;
8212                 gchar *accel_path = NULL;
8213                 item = gtk_menu_item_new_with_label(tmpl->name);
8214                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8215                 g_signal_connect(G_OBJECT(item), "activate",
8216                                  G_CALLBACK(compose_template_activate_cb),
8217                                  compose);
8218                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8219                 gtk_widget_show(item);
8220                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8221                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8222                 g_free(accel_path);
8223         }
8224
8225         gtk_widget_show(menu);
8226         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8227 }
8228
8229 void compose_update_actions_menu(Compose *compose)
8230 {
8231         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8232 }
8233
8234 static void compose_update_privacy_systems_menu(Compose *compose)
8235 {
8236         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8237         GSList *systems, *cur;
8238         GtkWidget *widget;
8239         GtkWidget *system_none;
8240         GSList *group;
8241         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8242         GtkWidget *privacy_menu = gtk_menu_new();
8243
8244         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8245         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8246
8247         g_signal_connect(G_OBJECT(system_none), "activate",
8248                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8249
8250         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8251         gtk_widget_show(system_none);
8252
8253         systems = privacy_get_system_ids();
8254         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8255                 gchar *systemid = cur->data;
8256
8257                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8258                 widget = gtk_radio_menu_item_new_with_label(group,
8259                         privacy_system_get_name(systemid));
8260                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8261                                        g_strdup(systemid), g_free);
8262                 g_signal_connect(G_OBJECT(widget), "activate",
8263                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8264
8265                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8266                 gtk_widget_show(widget);
8267                 g_free(systemid);
8268         }
8269         g_slist_free(systems);
8270         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8271         gtk_widget_show_all(privacy_menu);
8272         gtk_widget_show_all(privacy_menuitem);
8273 }
8274
8275 void compose_reflect_prefs_all(void)
8276 {
8277         GList *cur;
8278         Compose *compose;
8279
8280         for (cur = compose_list; cur != NULL; cur = cur->next) {
8281                 compose = (Compose *)cur->data;
8282                 compose_set_template_menu(compose);
8283         }
8284 }
8285
8286 void compose_reflect_prefs_pixmap_theme(void)
8287 {
8288         GList *cur;
8289         Compose *compose;
8290
8291         for (cur = compose_list; cur != NULL; cur = cur->next) {
8292                 compose = (Compose *)cur->data;
8293                 toolbar_update(TOOLBAR_COMPOSE, compose);
8294         }
8295 }
8296
8297 static const gchar *compose_quote_char_from_context(Compose *compose)
8298 {
8299         const gchar *qmark = NULL;
8300
8301         cm_return_val_if_fail(compose != NULL, NULL);
8302
8303         switch (compose->mode) {
8304                 /* use forward-specific quote char */
8305                 case COMPOSE_FORWARD:
8306                 case COMPOSE_FORWARD_AS_ATTACH:
8307                 case COMPOSE_FORWARD_INLINE:
8308                         if (compose->folder && compose->folder->prefs &&
8309                                         compose->folder->prefs->forward_with_format)
8310                                 qmark = compose->folder->prefs->forward_quotemark;
8311                         else if (compose->account->forward_with_format)
8312                                 qmark = compose->account->forward_quotemark;
8313                         else
8314                                 qmark = prefs_common.fw_quotemark;
8315                         break;
8316
8317                 /* use reply-specific quote char in all other modes */
8318                 default:
8319                         if (compose->folder && compose->folder->prefs &&
8320                                         compose->folder->prefs->reply_with_format)
8321                                 qmark = compose->folder->prefs->reply_quotemark;
8322                         else if (compose->account->reply_with_format)
8323                                 qmark = compose->account->reply_quotemark;
8324                         else
8325                                 qmark = prefs_common.quotemark;
8326                         break;
8327         }
8328
8329         if (qmark == NULL || *qmark == '\0')
8330                 qmark = "> ";
8331
8332         return qmark;
8333 }
8334
8335 static void compose_template_apply(Compose *compose, Template *tmpl,
8336                                    gboolean replace)
8337 {
8338         GtkTextView *text;
8339         GtkTextBuffer *buffer;
8340         GtkTextMark *mark;
8341         GtkTextIter iter;
8342         const gchar *qmark;
8343         gchar *parsed_str = NULL;
8344         gint cursor_pos = 0;
8345         const gchar *err_msg = _("The body of the template has an error at line %d.");
8346         if (!tmpl) return;
8347
8348         /* process the body */
8349
8350         text = GTK_TEXT_VIEW(compose->text);
8351         buffer = gtk_text_view_get_buffer(text);
8352
8353         if (tmpl->value) {
8354                 qmark = compose_quote_char_from_context(compose);
8355
8356                 if (compose->replyinfo != NULL) {
8357
8358                         if (replace)
8359                                 gtk_text_buffer_set_text(buffer, "", -1);
8360                         mark = gtk_text_buffer_get_insert(buffer);
8361                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8362
8363                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8364                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8365
8366                 } else if (compose->fwdinfo != NULL) {
8367
8368                         if (replace)
8369                                 gtk_text_buffer_set_text(buffer, "", -1);
8370                         mark = gtk_text_buffer_get_insert(buffer);
8371                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8372
8373                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8374                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8375
8376                 } else {
8377                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8378
8379                         GtkTextIter start, end;
8380                         gchar *tmp = NULL;
8381
8382                         gtk_text_buffer_get_start_iter(buffer, &start);
8383                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8384                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8385
8386                         /* clear the buffer now */
8387                         if (replace)
8388                                 gtk_text_buffer_set_text(buffer, "", -1);
8389
8390                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8391                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8392                         procmsg_msginfo_free( dummyinfo );
8393
8394                         g_free( tmp );
8395                 } 
8396         } else {
8397                 if (replace)
8398                         gtk_text_buffer_set_text(buffer, "", -1);
8399                 mark = gtk_text_buffer_get_insert(buffer);
8400                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8401         }       
8402
8403         if (replace && parsed_str && compose->account->auto_sig)
8404                 compose_insert_sig(compose, FALSE);
8405
8406         if (replace && parsed_str) {
8407                 gtk_text_buffer_get_start_iter(buffer, &iter);
8408                 gtk_text_buffer_place_cursor(buffer, &iter);
8409         }
8410         
8411         if (parsed_str) {
8412                 cursor_pos = quote_fmt_get_cursor_pos();
8413                 compose->set_cursor_pos = cursor_pos;
8414                 if (cursor_pos == -1)
8415                         cursor_pos = 0;
8416                 gtk_text_buffer_get_start_iter(buffer, &iter);
8417                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8418                 gtk_text_buffer_place_cursor(buffer, &iter);
8419         }
8420
8421         /* process the other fields */
8422
8423         compose_template_apply_fields(compose, tmpl);
8424         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8425         quote_fmt_reset_vartable();
8426         compose_changed_cb(NULL, compose);
8427
8428 #ifdef USE_ENCHANT
8429         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8430                 gtkaspell_highlight_all(compose->gtkaspell);
8431 #endif
8432 }
8433
8434 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8435 {
8436         MsgInfo* dummyinfo = NULL;
8437         MsgInfo *msginfo = NULL;
8438         gchar *buf = NULL;
8439
8440         if (compose->replyinfo != NULL)
8441                 msginfo = compose->replyinfo;
8442         else if (compose->fwdinfo != NULL)
8443                 msginfo = compose->fwdinfo;
8444         else {
8445                 dummyinfo = compose_msginfo_new_from_compose(compose);
8446                 msginfo = dummyinfo;
8447         }
8448
8449         if (tmpl->from && *tmpl->from != '\0') {
8450 #ifdef USE_ENCHANT
8451                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8452                                 compose->gtkaspell);
8453 #else
8454                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8455 #endif
8456                 quote_fmt_scan_string(tmpl->from);
8457                 quote_fmt_parse();
8458
8459                 buf = quote_fmt_get_buffer();
8460                 if (buf == NULL) {
8461                         alertpanel_error(_("Template From format error."));
8462                 } else {
8463                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8464                 }
8465         }
8466
8467         if (tmpl->to && *tmpl->to != '\0') {
8468 #ifdef USE_ENCHANT
8469                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8470                                 compose->gtkaspell);
8471 #else
8472                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8473 #endif
8474                 quote_fmt_scan_string(tmpl->to);
8475                 quote_fmt_parse();
8476
8477                 buf = quote_fmt_get_buffer();
8478                 if (buf == NULL) {
8479                         alertpanel_error(_("Template To format error."));
8480                 } else {
8481                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8482                 }
8483         }
8484
8485         if (tmpl->cc && *tmpl->cc != '\0') {
8486 #ifdef USE_ENCHANT
8487                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8488                                 compose->gtkaspell);
8489 #else
8490                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8491 #endif
8492                 quote_fmt_scan_string(tmpl->cc);
8493                 quote_fmt_parse();
8494
8495                 buf = quote_fmt_get_buffer();
8496                 if (buf == NULL) {
8497                         alertpanel_error(_("Template Cc format error."));
8498                 } else {
8499                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8500                 }
8501         }
8502
8503         if (tmpl->bcc && *tmpl->bcc != '\0') {
8504 #ifdef USE_ENCHANT
8505                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8506                                 compose->gtkaspell);
8507 #else
8508                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8509 #endif
8510                 quote_fmt_scan_string(tmpl->bcc);
8511                 quote_fmt_parse();
8512
8513                 buf = quote_fmt_get_buffer();
8514                 if (buf == NULL) {
8515                         alertpanel_error(_("Template Bcc format error."));
8516                 } else {
8517                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8518                 }
8519         }
8520
8521         /* process the subject */
8522         if (tmpl->subject && *tmpl->subject != '\0') {
8523 #ifdef USE_ENCHANT
8524                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8525                                 compose->gtkaspell);
8526 #else
8527                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8528 #endif
8529                 quote_fmt_scan_string(tmpl->subject);
8530                 quote_fmt_parse();
8531
8532                 buf = quote_fmt_get_buffer();
8533                 if (buf == NULL) {
8534                         alertpanel_error(_("Template subject format error."));
8535                 } else {
8536                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8537                 }
8538         }
8539
8540         procmsg_msginfo_free( dummyinfo );
8541 }
8542
8543 static void compose_destroy(Compose *compose)
8544 {
8545         GtkAllocation allocation;
8546         GtkTextBuffer *buffer;
8547         GtkClipboard *clipboard;
8548
8549         compose_list = g_list_remove(compose_list, compose);
8550
8551         if (compose->updating) {
8552                 debug_print("danger, not destroying anything now\n");
8553                 compose->deferred_destroy = TRUE;
8554                 return;
8555         }
8556         /* NOTE: address_completion_end() does nothing with the window
8557          * however this may change. */
8558         address_completion_end(compose->window);
8559
8560         slist_free_strings_full(compose->to_list);
8561         slist_free_strings_full(compose->newsgroup_list);
8562         slist_free_strings_full(compose->header_list);
8563
8564         slist_free_strings_full(extra_headers);
8565         extra_headers = NULL;
8566
8567         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8568
8569         g_hash_table_destroy(compose->email_hashtable);
8570
8571         procmsg_msginfo_free(compose->targetinfo);
8572         procmsg_msginfo_free(compose->replyinfo);
8573         procmsg_msginfo_free(compose->fwdinfo);
8574
8575         g_free(compose->replyto);
8576         g_free(compose->cc);
8577         g_free(compose->bcc);
8578         g_free(compose->newsgroups);
8579         g_free(compose->followup_to);
8580
8581         g_free(compose->ml_post);
8582
8583         g_free(compose->inreplyto);
8584         g_free(compose->references);
8585         g_free(compose->msgid);
8586         g_free(compose->boundary);
8587
8588         g_free(compose->redirect_filename);
8589         if (compose->undostruct)
8590                 undo_destroy(compose->undostruct);
8591
8592         g_free(compose->sig_str);
8593
8594         g_free(compose->exteditor_file);
8595
8596         g_free(compose->orig_charset);
8597
8598         g_free(compose->privacy_system);
8599
8600 #ifndef USE_NEW_ADDRBOOK
8601         if (addressbook_get_target_compose() == compose)
8602                 addressbook_set_target_compose(NULL);
8603 #endif
8604 #if USE_ENCHANT
8605         if (compose->gtkaspell) {
8606                 gtkaspell_delete(compose->gtkaspell);
8607                 compose->gtkaspell = NULL;
8608         }
8609 #endif
8610
8611         if (!compose->batch) {
8612                 gtk_widget_get_allocation(compose->window, &allocation);
8613                 prefs_common.compose_width = allocation.width;
8614                 prefs_common.compose_height = allocation.height;
8615         }
8616
8617         if (!gtk_widget_get_parent(compose->paned))
8618                 gtk_widget_destroy(compose->paned);
8619         gtk_widget_destroy(compose->popupmenu);
8620
8621         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8622         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8623         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8624
8625         gtk_widget_destroy(compose->window);
8626         toolbar_destroy(compose->toolbar);
8627         g_free(compose->toolbar);
8628         cm_mutex_free(compose->mutex);
8629         g_free(compose);
8630 }
8631
8632 static void compose_attach_info_free(AttachInfo *ainfo)
8633 {
8634         g_free(ainfo->file);
8635         g_free(ainfo->content_type);
8636         g_free(ainfo->name);
8637         g_free(ainfo->charset);
8638         g_free(ainfo);
8639 }
8640
8641 static void compose_attach_update_label(Compose *compose)
8642 {
8643         GtkTreeIter iter;
8644         gint i = 1;
8645         gchar *text;
8646         GtkTreeModel *model;
8647         
8648         if(compose == NULL)
8649                 return;
8650                 
8651         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8652         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8653                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8654                 return;
8655         }
8656         
8657         while(gtk_tree_model_iter_next(model, &iter))
8658                 i++;
8659         
8660         text = g_strdup_printf("(%d)", i);
8661         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8662         g_free(text);
8663 }
8664
8665 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8666 {
8667         Compose *compose = (Compose *)data;
8668         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8669         GtkTreeSelection *selection;
8670         GList *sel, *cur;
8671         GtkTreeModel *model;
8672
8673         selection = gtk_tree_view_get_selection(tree_view);
8674         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8675
8676         if (!sel) 
8677                 return;
8678
8679         for (cur = sel; cur != NULL; cur = cur->next) {
8680                 GtkTreePath *path = cur->data;
8681                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8682                                                 (model, cur->data);
8683                 cur->data = ref;
8684                 gtk_tree_path_free(path);
8685         }
8686
8687         for (cur = sel; cur != NULL; cur = cur->next) {
8688                 GtkTreeRowReference *ref = cur->data;
8689                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8690                 GtkTreeIter iter;
8691
8692                 if (gtk_tree_model_get_iter(model, &iter, path))
8693                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8694                 
8695                 gtk_tree_path_free(path);
8696                 gtk_tree_row_reference_free(ref);
8697         }
8698
8699         g_list_free(sel);
8700         compose_attach_update_label(compose);
8701 }
8702
8703 static struct _AttachProperty
8704 {
8705         GtkWidget *window;
8706         GtkWidget *mimetype_entry;
8707         GtkWidget *encoding_optmenu;
8708         GtkWidget *path_entry;
8709         GtkWidget *filename_entry;
8710         GtkWidget *ok_btn;
8711         GtkWidget *cancel_btn;
8712 } attach_prop;
8713
8714 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8715 {       
8716         gtk_tree_path_free((GtkTreePath *)ptr);
8717 }
8718
8719 static void compose_attach_property(GtkAction *action, gpointer data)
8720 {
8721         Compose *compose = (Compose *)data;
8722         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8723         AttachInfo *ainfo;
8724         GtkComboBox *optmenu;
8725         GtkTreeSelection *selection;
8726         GList *sel;
8727         GtkTreeModel *model;
8728         GtkTreeIter iter;
8729         GtkTreePath *path;
8730         static gboolean cancelled;
8731
8732         /* only if one selected */
8733         selection = gtk_tree_view_get_selection(tree_view);
8734         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8735                 return;
8736
8737         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8738         if (!sel)
8739                 return;
8740
8741         path = (GtkTreePath *) sel->data;
8742         gtk_tree_model_get_iter(model, &iter, path);
8743         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8744         
8745         if (!ainfo) {
8746                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8747                 g_list_free(sel);
8748                 return;
8749         }               
8750         g_list_free(sel);
8751
8752         if (!attach_prop.window)
8753                 compose_attach_property_create(&cancelled);
8754         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8755         gtk_widget_grab_focus(attach_prop.ok_btn);
8756         gtk_widget_show(attach_prop.window);
8757         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8758                         GTK_WINDOW(compose->window));
8759
8760         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8761         if (ainfo->encoding == ENC_UNKNOWN)
8762                 combobox_select_by_data(optmenu, ENC_BASE64);
8763         else
8764                 combobox_select_by_data(optmenu, ainfo->encoding);
8765
8766         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8767                            ainfo->content_type ? ainfo->content_type : "");
8768         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8769                            ainfo->file ? ainfo->file : "");
8770         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8771                            ainfo->name ? ainfo->name : "");
8772
8773         for (;;) {
8774                 const gchar *entry_text;
8775                 gchar *text;
8776                 gchar *cnttype = NULL;
8777                 gchar *file = NULL;
8778                 off_t size = 0;
8779
8780                 cancelled = FALSE;
8781                 gtk_main();
8782
8783                 gtk_widget_hide(attach_prop.window);
8784                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8785                 
8786                 if (cancelled) 
8787                         break;
8788
8789                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8790                 if (*entry_text != '\0') {
8791                         gchar *p;
8792
8793                         text = g_strstrip(g_strdup(entry_text));
8794                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8795                                 cnttype = g_strdup(text);
8796                                 g_free(text);
8797                         } else {
8798                                 alertpanel_error(_("Invalid MIME type."));
8799                                 g_free(text);
8800                                 continue;
8801                         }
8802                 }
8803
8804                 ainfo->encoding = combobox_get_active_data(optmenu);
8805
8806                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8807                 if (*entry_text != '\0') {
8808                         if (is_file_exist(entry_text) &&
8809                             (size = get_file_size(entry_text)) > 0)
8810                                 file = g_strdup(entry_text);
8811                         else {
8812                                 alertpanel_error
8813                                         (_("File doesn't exist or is empty."));
8814                                 g_free(cnttype);
8815                                 continue;
8816                         }
8817                 }
8818
8819                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8820                 if (*entry_text != '\0') {
8821                         g_free(ainfo->name);
8822                         ainfo->name = g_strdup(entry_text);
8823                 }
8824
8825                 if (cnttype) {
8826                         g_free(ainfo->content_type);
8827                         ainfo->content_type = cnttype;
8828                 }
8829                 if (file) {
8830                         g_free(ainfo->file);
8831                         ainfo->file = file;
8832                 }
8833                 if (size)
8834                         ainfo->size = (goffset)size;
8835
8836                 /* update tree store */
8837                 text = to_human_readable(ainfo->size);
8838                 gtk_tree_model_get_iter(model, &iter, path);
8839                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8840                                    COL_MIMETYPE, ainfo->content_type,
8841                                    COL_SIZE, text,
8842                                    COL_NAME, ainfo->name,
8843                                    COL_CHARSET, ainfo->charset,
8844                                    -1);
8845                 
8846                 break;
8847         }
8848
8849         gtk_tree_path_free(path);
8850 }
8851
8852 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8853 { \
8854         label = gtk_label_new(str); \
8855         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8856                          GTK_FILL, 0, 0, 0); \
8857         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8858  \
8859         entry = gtk_entry_new(); \
8860         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8861                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8862 }
8863
8864 static void compose_attach_property_create(gboolean *cancelled)
8865 {
8866         GtkWidget *window;
8867         GtkWidget *vbox;
8868         GtkWidget *table;
8869         GtkWidget *label;
8870         GtkWidget *mimetype_entry;
8871         GtkWidget *hbox;
8872         GtkWidget *optmenu;
8873         GtkListStore *optmenu_menu;
8874         GtkWidget *path_entry;
8875         GtkWidget *filename_entry;
8876         GtkWidget *hbbox;
8877         GtkWidget *ok_btn;
8878         GtkWidget *cancel_btn;
8879         GList     *mime_type_list, *strlist;
8880         GtkTreeIter iter;
8881
8882         debug_print("Creating attach_property window...\n");
8883
8884         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8885         gtk_widget_set_size_request(window, 480, -1);
8886         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8887         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8888         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8889         g_signal_connect(G_OBJECT(window), "delete_event",
8890                          G_CALLBACK(attach_property_delete_event),
8891                          cancelled);
8892         g_signal_connect(G_OBJECT(window), "key_press_event",
8893                          G_CALLBACK(attach_property_key_pressed),
8894                          cancelled);
8895
8896         vbox = gtk_vbox_new(FALSE, 8);
8897         gtk_container_add(GTK_CONTAINER(window), vbox);
8898
8899         table = gtk_table_new(4, 2, FALSE);
8900         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8901         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8902         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8903
8904         label = gtk_label_new(_("MIME type")); 
8905         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8906                          GTK_FILL, 0, 0, 0); 
8907         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8908 #if !GTK_CHECK_VERSION(2, 24, 0)
8909         mimetype_entry = gtk_combo_box_entry_new_text(); 
8910 #else
8911         mimetype_entry = gtk_combo_box_text_new_with_entry();
8912 #endif
8913         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8914                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8915                          
8916         /* stuff with list */
8917         mime_type_list = procmime_get_mime_type_list();
8918         strlist = NULL;
8919         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8920                 MimeType *type = (MimeType *) mime_type_list->data;
8921                 gchar *tmp;
8922
8923                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8924
8925                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8926                         g_free(tmp);
8927                 else
8928                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8929                                         (GCompareFunc)strcmp2);
8930         }
8931
8932         for (mime_type_list = strlist; mime_type_list != NULL; 
8933                 mime_type_list = mime_type_list->next) {
8934 #if !GTK_CHECK_VERSION(2, 24, 0)
8935                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8936 #else
8937                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
8938 #endif
8939                 g_free(mime_type_list->data);
8940         }
8941         g_list_free(strlist);
8942         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8943         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8944
8945         label = gtk_label_new(_("Encoding"));
8946         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8947                          GTK_FILL, 0, 0, 0);
8948         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8949
8950         hbox = gtk_hbox_new(FALSE, 0);
8951         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8952                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8953
8954         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8955         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8956
8957         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8958         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8959         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8960         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8961         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8962
8963         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8964
8965         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8966         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8967
8968         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8969                                       &ok_btn, GTK_STOCK_OK,
8970                                       NULL, NULL);
8971         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8972         gtk_widget_grab_default(ok_btn);
8973
8974         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8975                          G_CALLBACK(attach_property_ok),
8976                          cancelled);
8977         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8978                          G_CALLBACK(attach_property_cancel),
8979                          cancelled);
8980
8981         gtk_widget_show_all(vbox);
8982
8983         attach_prop.window           = window;
8984         attach_prop.mimetype_entry   = mimetype_entry;
8985         attach_prop.encoding_optmenu = optmenu;
8986         attach_prop.path_entry       = path_entry;
8987         attach_prop.filename_entry   = filename_entry;
8988         attach_prop.ok_btn           = ok_btn;
8989         attach_prop.cancel_btn       = cancel_btn;
8990 }
8991
8992 #undef SET_LABEL_AND_ENTRY
8993
8994 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8995 {
8996         *cancelled = FALSE;
8997         gtk_main_quit();
8998 }
8999
9000 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9001 {
9002         *cancelled = TRUE;
9003         gtk_main_quit();
9004 }
9005
9006 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9007                                          gboolean *cancelled)
9008 {
9009         *cancelled = TRUE;
9010         gtk_main_quit();
9011
9012         return TRUE;
9013 }
9014
9015 static gboolean attach_property_key_pressed(GtkWidget *widget,
9016                                             GdkEventKey *event,
9017                                             gboolean *cancelled)
9018 {
9019         if (event && event->keyval == GDK_KEY_Escape) {
9020                 *cancelled = TRUE;
9021                 gtk_main_quit();
9022         }
9023         if (event && event->keyval == GDK_KEY_Return) {
9024                 *cancelled = FALSE;
9025                 gtk_main_quit();
9026                 return TRUE;
9027         }
9028         return FALSE;
9029 }
9030
9031 static void compose_exec_ext_editor(Compose *compose)
9032 {
9033 #ifdef G_OS_UNIX
9034         gchar *tmp;
9035         pid_t pid;
9036         gint pipe_fds[2];
9037
9038         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9039                               G_DIR_SEPARATOR, compose);
9040
9041         if (pipe(pipe_fds) < 0) {
9042                 perror("pipe");
9043                 g_free(tmp);
9044                 return;
9045         }
9046
9047         if ((pid = fork()) < 0) {
9048                 perror("fork");
9049                 g_free(tmp);
9050                 return;
9051         }
9052
9053         if (pid != 0) {
9054                 /* close the write side of the pipe */
9055                 close(pipe_fds[1]);
9056
9057                 compose->exteditor_file    = g_strdup(tmp);
9058                 compose->exteditor_pid     = pid;
9059
9060                 compose_set_ext_editor_sensitive(compose, FALSE);
9061
9062 #ifndef G_OS_WIN32
9063                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9064 #else
9065                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9066 #endif
9067                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9068                                                         G_IO_IN,
9069                                                         compose_input_cb,
9070                                                         compose);
9071         } else {        /* process-monitoring process */
9072                 pid_t pid_ed;
9073
9074                 if (setpgid(0, 0))
9075                         perror("setpgid");
9076
9077                 /* close the read side of the pipe */
9078                 close(pipe_fds[0]);
9079
9080                 if (compose_write_body_to_file(compose, tmp) < 0) {
9081                         fd_write_all(pipe_fds[1], "2\n", 2);
9082                         _exit(1);
9083                 }
9084
9085                 pid_ed = compose_exec_ext_editor_real(tmp);
9086                 if (pid_ed < 0) {
9087                         fd_write_all(pipe_fds[1], "1\n", 2);
9088                         _exit(1);
9089                 }
9090
9091                 /* wait until editor is terminated */
9092                 waitpid(pid_ed, NULL, 0);
9093
9094                 fd_write_all(pipe_fds[1], "0\n", 2);
9095
9096                 close(pipe_fds[1]);
9097                 _exit(0);
9098         }
9099
9100         g_free(tmp);
9101 #endif /* G_OS_UNIX */
9102 }
9103
9104 #ifdef G_OS_UNIX
9105 static gint compose_exec_ext_editor_real(const gchar *file)
9106 {
9107         gchar buf[1024];
9108         gchar *p;
9109         gchar **cmdline;
9110         pid_t pid;
9111
9112         cm_return_val_if_fail(file != NULL, -1);
9113
9114         if ((pid = fork()) < 0) {
9115                 perror("fork");
9116                 return -1;
9117         }
9118
9119         if (pid != 0) return pid;
9120
9121         /* grandchild process */
9122
9123         if (setpgid(0, getppid()))
9124                 perror("setpgid");
9125
9126         if (prefs_common_get_ext_editor_cmd() &&
9127             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9128             *(p + 1) == 's' && !strchr(p + 2, '%')) {
9129                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9130         } else {
9131                 if (prefs_common_get_ext_editor_cmd())
9132                         g_warning("External editor command-line is invalid: '%s'\n",
9133                                   prefs_common_get_ext_editor_cmd());
9134                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9135         }
9136
9137         cmdline = strsplit_with_quote(buf, " ", 1024);
9138         execvp(cmdline[0], cmdline);
9139
9140         perror("execvp");
9141         g_strfreev(cmdline);
9142
9143         _exit(1);
9144 }
9145
9146 static gboolean compose_ext_editor_kill(Compose *compose)
9147 {
9148         pid_t pgid = compose->exteditor_pid * -1;
9149         gint ret;
9150
9151         ret = kill(pgid, 0);
9152
9153         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9154                 AlertValue val;
9155                 gchar *msg;
9156
9157                 msg = g_strdup_printf
9158                         (_("The external editor is still working.\n"
9159                            "Force terminating the process?\n"
9160                            "process group id: %d"), -pgid);
9161                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9162                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9163                         
9164                 g_free(msg);
9165
9166                 if (val == G_ALERTALTERNATE) {
9167                         g_source_remove(compose->exteditor_tag);
9168                         g_io_channel_shutdown(compose->exteditor_ch,
9169                                               FALSE, NULL);
9170                         g_io_channel_unref(compose->exteditor_ch);
9171
9172                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9173                         waitpid(compose->exteditor_pid, NULL, 0);
9174
9175                         g_warning("Terminated process group id: %d", -pgid);
9176                         g_warning("Temporary file: %s",
9177                                   compose->exteditor_file);
9178
9179                         compose_set_ext_editor_sensitive(compose, TRUE);
9180
9181                         g_free(compose->exteditor_file);
9182                         compose->exteditor_file    = NULL;
9183                         compose->exteditor_pid     = -1;
9184                         compose->exteditor_ch      = NULL;
9185                         compose->exteditor_tag     = -1;
9186                 } else
9187                         return FALSE;
9188         }
9189
9190         return TRUE;
9191 }
9192
9193 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9194                                  gpointer data)
9195 {
9196         gchar buf[3] = "3";
9197         Compose *compose = (Compose *)data;
9198         gsize bytes_read;
9199
9200         debug_print("Compose: input from monitoring process\n");
9201
9202         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9203
9204         g_io_channel_shutdown(source, FALSE, NULL);
9205         g_io_channel_unref(source);
9206
9207         waitpid(compose->exteditor_pid, NULL, 0);
9208
9209         if (buf[0] == '0') {            /* success */
9210                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9211                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9212
9213                 gtk_text_buffer_set_text(buffer, "", -1);
9214                 compose_insert_file(compose, compose->exteditor_file);
9215                 compose_changed_cb(NULL, compose);
9216                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9217
9218                 if (claws_unlink(compose->exteditor_file) < 0)
9219                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9220         } else if (buf[0] == '1') {     /* failed */
9221                 g_warning("Couldn't exec external editor\n");
9222                 if (claws_unlink(compose->exteditor_file) < 0)
9223                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9224         } else if (buf[0] == '2') {
9225                 g_warning("Couldn't write to file\n");
9226         } else if (buf[0] == '3') {
9227                 g_warning("Pipe read failed\n");
9228         }
9229
9230         compose_set_ext_editor_sensitive(compose, TRUE);
9231
9232         g_free(compose->exteditor_file);
9233         compose->exteditor_file    = NULL;
9234         compose->exteditor_pid     = -1;
9235         compose->exteditor_ch      = NULL;
9236         compose->exteditor_tag     = -1;
9237
9238         return FALSE;
9239 }
9240
9241 static void compose_set_ext_editor_sensitive(Compose *compose,
9242                                              gboolean sensitive)
9243 {
9244         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9245         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9246         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9247         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9248         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9249         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9250         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9251
9252         gtk_widget_set_sensitive(compose->text,                       sensitive);
9253         if (compose->toolbar->send_btn)
9254                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9255         if (compose->toolbar->sendl_btn)
9256                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9257         if (compose->toolbar->draft_btn)
9258                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9259         if (compose->toolbar->insert_btn)
9260                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9261         if (compose->toolbar->sig_btn)
9262                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9263         if (compose->toolbar->exteditor_btn)
9264                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9265         if (compose->toolbar->linewrap_current_btn)
9266                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9267         if (compose->toolbar->linewrap_all_btn)
9268                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9269 }
9270 #endif /* G_OS_UNIX */
9271
9272 /**
9273  * compose_undo_state_changed:
9274  *
9275  * Change the sensivity of the menuentries undo and redo
9276  **/
9277 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9278                                        gint redo_state, gpointer data)
9279 {
9280         Compose *compose = (Compose *)data;
9281
9282         switch (undo_state) {
9283         case UNDO_STATE_TRUE:
9284                 if (!undostruct->undo_state) {
9285                         undostruct->undo_state = TRUE;
9286                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9287                 }
9288                 break;
9289         case UNDO_STATE_FALSE:
9290                 if (undostruct->undo_state) {
9291                         undostruct->undo_state = FALSE;
9292                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9293                 }
9294                 break;
9295         case UNDO_STATE_UNCHANGED:
9296                 break;
9297         case UNDO_STATE_REFRESH:
9298                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9299                 break;
9300         default:
9301                 g_warning("Undo state not recognized");
9302                 break;
9303         }
9304
9305         switch (redo_state) {
9306         case UNDO_STATE_TRUE:
9307                 if (!undostruct->redo_state) {
9308                         undostruct->redo_state = TRUE;
9309                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9310                 }
9311                 break;
9312         case UNDO_STATE_FALSE:
9313                 if (undostruct->redo_state) {
9314                         undostruct->redo_state = FALSE;
9315                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9316                 }
9317                 break;
9318         case UNDO_STATE_UNCHANGED:
9319                 break;
9320         case UNDO_STATE_REFRESH:
9321                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9322                 break;
9323         default:
9324                 g_warning("Redo state not recognized");
9325                 break;
9326         }
9327 }
9328
9329 /* callback functions */
9330
9331 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9332                                         GtkAllocation *allocation,
9333                                         Compose *compose)
9334 {
9335         prefs_common.compose_notebook_height = allocation->height;
9336 }
9337
9338 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9339  * includes "non-client" (windows-izm) in calculation, so this calculation
9340  * may not be accurate.
9341  */
9342 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9343                                         GtkAllocation *allocation,
9344                                         GtkSHRuler *shruler)
9345 {
9346         if (prefs_common.show_ruler) {
9347                 gint char_width = 0, char_height = 0;
9348                 gint line_width_in_chars;
9349
9350                 gtkut_get_font_size(GTK_WIDGET(widget),
9351                                     &char_width, &char_height);
9352                 line_width_in_chars =
9353                         (allocation->width - allocation->x) / char_width;
9354
9355                 /* got the maximum */
9356                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9357                                     0.0, line_width_in_chars, 0);
9358         }
9359
9360         return TRUE;
9361 }
9362
9363 typedef struct {
9364         gchar                   *header;
9365         gchar                   *entry;
9366         ComposePrefType         type;
9367         gboolean                entry_marked;
9368 } HeaderEntryState;
9369
9370 static void account_activated(GtkComboBox *optmenu, gpointer data)
9371 {
9372         Compose *compose = (Compose *)data;
9373
9374         PrefsAccount *ac;
9375         gchar *folderidentifier;
9376         gint account_id = 0;
9377         GtkTreeModel *menu;
9378         GtkTreeIter iter;
9379         GSList *list, *saved_list = NULL;
9380         HeaderEntryState *state;
9381         GtkRcStyle *style = NULL;
9382 #if !GTK_CHECK_VERSION(3, 0, 0)
9383         static GdkColor yellow;
9384         static gboolean color_set = FALSE;
9385 #else
9386         static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9387 #endif
9388
9389         /* Get ID of active account in the combo box */
9390         menu = gtk_combo_box_get_model(optmenu);
9391         gtk_combo_box_get_active_iter(optmenu, &iter);
9392         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9393
9394         ac = account_find_from_id(account_id);
9395         cm_return_if_fail(ac != NULL);
9396
9397         if (ac != compose->account) {
9398                 compose_select_account(compose, ac, FALSE);
9399
9400                 for (list = compose->header_list; list; list = list->next) {
9401                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9402                         
9403                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9404                                 compose_destroy_headerentry(compose, hentry);
9405                                 continue;
9406                         }
9407                         
9408                         state = g_malloc0(sizeof(HeaderEntryState));
9409                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9410                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9411                         state->entry = gtk_editable_get_chars(
9412                                         GTK_EDITABLE(hentry->entry), 0, -1);
9413                         state->type = hentry->type;
9414                                 
9415 #if !GTK_CHECK_VERSION(3, 0, 0)
9416                         if (!color_set) {
9417                                 gdk_color_parse("#f5f6be", &yellow);
9418                                 color_set = gdk_colormap_alloc_color(
9419                                                         gdk_colormap_get_system(),
9420                                                         &yellow, FALSE, TRUE);
9421                         }
9422 #endif
9423                                 
9424                         style = gtk_widget_get_modifier_style(hentry->entry);
9425                         state->entry_marked = gdk_color_equal(&yellow,
9426                                                 &style->base[GTK_STATE_NORMAL]);
9427
9428                         saved_list = g_slist_append(saved_list, state);
9429                         compose_destroy_headerentry(compose, hentry);
9430                 }
9431
9432                 compose->header_last = NULL;
9433                 g_slist_free(compose->header_list);
9434                 compose->header_list = NULL;
9435                 compose->header_nextrow = 1;
9436                 compose_create_header_entry(compose);
9437                 
9438                 if (ac->set_autocc && ac->auto_cc)
9439                         compose_entry_append(compose, ac->auto_cc,
9440                                                 COMPOSE_CC, PREF_ACCOUNT);
9441
9442                 if (ac->set_autobcc && ac->auto_bcc) 
9443                         compose_entry_append(compose, ac->auto_bcc,
9444                                                 COMPOSE_BCC, PREF_ACCOUNT);
9445         
9446                 if (ac->set_autoreplyto && ac->auto_replyto)
9447                         compose_entry_append(compose, ac->auto_replyto,
9448                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9449                 
9450                 for (list = saved_list; list; list = list->next) {
9451                         state = (HeaderEntryState *) list->data;
9452                         
9453                         compose_add_header_entry(compose, state->header,
9454                                                 state->entry, state->type);
9455                         if (state->entry_marked)
9456                                 compose_entry_mark_default_to(compose, state->entry);
9457                         
9458                         g_free(state->header);  
9459                         g_free(state->entry);
9460                         g_free(state);
9461                 }
9462                 g_slist_free(saved_list);
9463                 
9464                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9465                                         (ac->protocol == A_NNTP) ? 
9466                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9467         }
9468
9469         /* Set message save folder */
9470         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9471                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9472         }
9473         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9474                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9475                            
9476         compose_set_save_to(compose, NULL);
9477         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9478                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9479                                   (compose->account, F_OUTBOX));
9480                 compose_set_save_to(compose, folderidentifier);
9481                 g_free(folderidentifier);
9482         }
9483 }
9484
9485 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9486                             GtkTreeViewColumn *column, Compose *compose)
9487 {
9488         compose_attach_property(NULL, compose);
9489 }
9490
9491 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9492                                       gpointer data)
9493 {
9494         Compose *compose = (Compose *)data;
9495         GtkTreeSelection *attach_selection;
9496         gint attach_nr_selected;
9497         
9498         if (!event) return FALSE;
9499
9500         if (event->button == 3) {
9501                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9502                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9503                         
9504                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9505                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9506                         
9507                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9508                                NULL, NULL, event->button, event->time);
9509                 return TRUE;                           
9510         }
9511
9512         return FALSE;
9513 }
9514
9515 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9516                                    gpointer data)
9517 {
9518         Compose *compose = (Compose *)data;
9519
9520         if (!event) return FALSE;
9521
9522         switch (event->keyval) {
9523         case GDK_KEY_Delete:
9524                 compose_attach_remove_selected(NULL, compose);
9525                 break;
9526         }
9527         return FALSE;
9528 }
9529
9530 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9531 {
9532         toolbar_comp_set_sensitive(compose, allow);
9533         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9534         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9535 #if USE_ENCHANT
9536         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9537 #endif  
9538         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9539         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9540         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9541         
9542         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9543
9544 }
9545
9546 static void compose_send_cb(GtkAction *action, gpointer data)
9547 {
9548         Compose *compose = (Compose *)data;
9549
9550         if (prefs_common.work_offline && 
9551             !inc_offline_should_override(TRUE,
9552                 _("Claws Mail needs network access in order "
9553                   "to send this email.")))
9554                 return;
9555         
9556         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9557                 g_source_remove(compose->draft_timeout_tag);
9558                 compose->draft_timeout_tag = -1;
9559         }
9560
9561         compose_send(compose);
9562 }
9563
9564 static void compose_send_later_cb(GtkAction *action, gpointer data)
9565 {
9566         Compose *compose = (Compose *)data;
9567         gint val;
9568
9569         inc_lock();
9570         compose_allow_user_actions(compose, FALSE);
9571         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9572         compose_allow_user_actions(compose, TRUE);
9573         inc_unlock();
9574
9575         if (!val) {
9576                 compose_close(compose);
9577         } else if (val == -1) {
9578                 alertpanel_error(_("Could not queue message."));
9579         } else if (val == -2) {
9580                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9581         } else if (val == -3) {
9582                 if (privacy_peek_error())
9583                 alertpanel_error(_("Could not queue message for sending:\n\n"
9584                                    "Signature failed: %s"), privacy_get_error());
9585         } else if (val == -4) {
9586                 alertpanel_error(_("Could not queue message for sending:\n\n"
9587                                    "Charset conversion failed."));
9588         } else if (val == -5) {
9589                 alertpanel_error(_("Could not queue message for sending:\n\n"
9590                                    "Couldn't get recipient encryption key."));
9591         } else if (val == -6) {
9592                 /* silent error */
9593         }
9594         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9595 }
9596
9597 #define DRAFTED_AT_EXIT "drafted_at_exit"
9598 static void compose_register_draft(MsgInfo *info)
9599 {
9600         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9601                                       DRAFTED_AT_EXIT, NULL);
9602         FILE *fp = g_fopen(filepath, "ab");
9603         
9604         if (fp) {
9605                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9606                                 info->msgnum);
9607                 fclose(fp);
9608         }
9609                 
9610         g_free(filepath);       
9611 }
9612
9613 gboolean compose_draft (gpointer data, guint action) 
9614 {
9615         Compose *compose = (Compose *)data;
9616         FolderItem *draft;
9617         gchar *tmp;
9618         gchar *sheaders;
9619         gint msgnum;
9620         MsgFlags flag = {0, 0};
9621         static gboolean lock = FALSE;
9622         MsgInfo *newmsginfo;
9623         FILE *fp;
9624         gboolean target_locked = FALSE;
9625         gboolean err = FALSE;
9626
9627         if (lock) return FALSE;
9628
9629         if (compose->sending)
9630                 return TRUE;
9631
9632         draft = account_get_special_folder(compose->account, F_DRAFT);
9633         cm_return_val_if_fail(draft != NULL, FALSE);
9634         
9635         if (!g_mutex_trylock(compose->mutex)) {
9636                 /* we don't want to lock the mutex once it's available,
9637                  * because as the only other part of compose.c locking
9638                  * it is compose_close - which means once unlocked,
9639                  * the compose struct will be freed */
9640                 debug_print("couldn't lock mutex, probably sending\n");
9641                 return FALSE;
9642         }
9643         
9644         lock = TRUE;
9645
9646         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9647                               G_DIR_SEPARATOR, compose);
9648         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9649                 FILE_OP_ERROR(tmp, "fopen");
9650                 goto warn_err;
9651         }
9652
9653         /* chmod for security */
9654         if (change_file_mode_rw(fp, tmp) < 0) {
9655                 FILE_OP_ERROR(tmp, "chmod");
9656                 g_warning("can't change file mode\n");
9657         }
9658
9659         /* Save draft infos */
9660         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9661         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9662
9663         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9664                 gchar *savefolderid;
9665
9666                 savefolderid = compose_get_save_to(compose);
9667                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9668                 g_free(savefolderid);
9669         }
9670         if (compose->return_receipt) {
9671                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9672         }
9673         if (compose->privacy_system) {
9674                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9675                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9676                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9677         }
9678
9679         /* Message-ID of message replying to */
9680         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9681                 gchar *folderid;
9682                 
9683                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9684                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9685                 g_free(folderid);
9686         }
9687         /* Message-ID of message forwarding to */
9688         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9689                 gchar *folderid;
9690                 
9691                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9692                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9693                 g_free(folderid);
9694         }
9695
9696         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9697         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9698
9699         sheaders = compose_get_manual_headers_info(compose);
9700         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9701         g_free(sheaders);
9702
9703         /* end of headers */
9704         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9705
9706         if (err) {
9707                 fclose(fp);
9708                 goto warn_err;
9709         }
9710
9711         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9712                 fclose(fp);
9713                 goto warn_err;
9714         }
9715         if (fclose(fp) == EOF) {
9716                 goto warn_err;
9717         }
9718         
9719         if (compose->targetinfo) {
9720                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9721                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9722         }
9723         flag.tmp_flags = MSG_DRAFT;
9724
9725         folder_item_scan(draft);
9726         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9727                 MsgInfo *tmpinfo = NULL;
9728                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9729                 if (compose->msgid) {
9730                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9731                 }
9732                 if (tmpinfo) {
9733                         msgnum = tmpinfo->msgnum;
9734                         procmsg_msginfo_free(tmpinfo);
9735                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9736                 } else {
9737                         debug_print("didn't get draft msgnum after scanning\n");
9738                 }
9739         } else {
9740                 debug_print("got draft msgnum %d from adding\n", msgnum);
9741         }
9742         if (msgnum < 0) {
9743 warn_err:
9744                 claws_unlink(tmp);
9745                 g_free(tmp);
9746                 if (action != COMPOSE_AUTO_SAVE) {
9747                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9748                                 alertpanel_error(_("Could not save draft."));
9749                         else {
9750                                 AlertValue val;
9751                                 gtkut_window_popup(compose->window);
9752                                 val = alertpanel_full(_("Could not save draft"),
9753                                         _("Could not save draft.\n"
9754                                         "Do you want to cancel exit or discard this email?"),
9755                                           _("_Cancel exit"), _("_Discard email"), NULL,
9756                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9757                                 if (val == G_ALERTALTERNATE) {
9758                                         lock = FALSE;
9759                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9760                                         compose_close(compose);
9761                                         return TRUE;
9762                                 } else {
9763                                         lock = FALSE;
9764                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9765                                         return FALSE;
9766                                 }
9767                         }
9768                 }
9769                 goto unlock;
9770         }
9771         g_free(tmp);
9772
9773         if (compose->mode == COMPOSE_REEDIT) {
9774                 compose_remove_reedit_target(compose, TRUE);
9775         }
9776
9777         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9778
9779         if (newmsginfo) {
9780                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9781                 if (target_locked)
9782                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9783                 else
9784                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9785                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9786                         procmsg_msginfo_set_flags(newmsginfo, 0,
9787                                                   MSG_HAS_ATTACHMENT);
9788
9789                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9790                         compose_register_draft(newmsginfo);
9791                 }
9792                 procmsg_msginfo_free(newmsginfo);
9793         }
9794         
9795         folder_item_scan(draft);
9796         
9797         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9798                 lock = FALSE;
9799                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9800                 compose_close(compose);
9801                 return TRUE;
9802         } else {
9803                 struct stat s;
9804                 gchar *path;
9805
9806                 path = folder_item_fetch_msg(draft, msgnum);
9807                 if (path == NULL) {
9808                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9809                         goto unlock;
9810                 }
9811                 if (g_stat(path, &s) < 0) {
9812                         FILE_OP_ERROR(path, "stat");
9813                         g_free(path);
9814                         goto unlock;
9815                 }
9816                 g_free(path);
9817
9818                 procmsg_msginfo_free(compose->targetinfo);
9819                 compose->targetinfo = procmsg_msginfo_new();
9820                 compose->targetinfo->msgnum = msgnum;
9821                 compose->targetinfo->size = (goffset)s.st_size;
9822                 compose->targetinfo->mtime = s.st_mtime;
9823                 compose->targetinfo->folder = draft;
9824                 if (target_locked)
9825                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9826                 compose->mode = COMPOSE_REEDIT;
9827                 
9828                 if (action == COMPOSE_AUTO_SAVE) {
9829                         compose->autosaved_draft = compose->targetinfo;
9830                 }
9831                 compose->modified = FALSE;
9832                 compose_set_title(compose);
9833         }
9834 unlock:
9835         lock = FALSE;
9836         g_mutex_unlock(compose->mutex);
9837         return TRUE;
9838 }
9839
9840 void compose_clear_exit_drafts(void)
9841 {
9842         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9843                                       DRAFTED_AT_EXIT, NULL);
9844         if (is_file_exist(filepath))
9845                 claws_unlink(filepath);
9846         
9847         g_free(filepath);
9848 }
9849
9850 void compose_reopen_exit_drafts(void)
9851 {
9852         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9853                                       DRAFTED_AT_EXIT, NULL);
9854         FILE *fp = g_fopen(filepath, "rb");
9855         gchar buf[1024];
9856         
9857         if (fp) {
9858                 while (fgets(buf, sizeof(buf), fp)) {
9859                         gchar **parts = g_strsplit(buf, "\t", 2);
9860                         const gchar *folder = parts[0];
9861                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9862                         
9863                         if (folder && *folder && msgnum > -1) {
9864                                 FolderItem *item = folder_find_item_from_identifier(folder);
9865                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9866                                 if (info)
9867                                         compose_reedit(info, FALSE);
9868                         }
9869                         g_strfreev(parts);
9870                 }       
9871                 fclose(fp);
9872         }       
9873         g_free(filepath);
9874         compose_clear_exit_drafts();
9875 }
9876
9877 static void compose_save_cb(GtkAction *action, gpointer data)
9878 {
9879         Compose *compose = (Compose *)data;
9880         compose_draft(compose, COMPOSE_KEEP_EDITING);
9881         compose->rmode = COMPOSE_REEDIT;
9882 }
9883
9884 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9885 {
9886         if (compose && file_list) {
9887                 GList *tmp;
9888
9889                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9890                         gchar *file = (gchar *) tmp->data;
9891                         gchar *utf8_filename = conv_filename_to_utf8(file);
9892                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9893                         compose_changed_cb(NULL, compose);
9894                         if (free_data) {
9895                         g_free(file);
9896                                 tmp->data = NULL;
9897                         }
9898                         g_free(utf8_filename);
9899                 }
9900         }
9901 }
9902
9903 static void compose_attach_cb(GtkAction *action, gpointer data)
9904 {
9905         Compose *compose = (Compose *)data;
9906         GList *file_list;
9907
9908         if (compose->redirect_filename != NULL)
9909                 return;
9910
9911         /* Set focus_window properly, in case we were called via popup menu,
9912          * which unsets it (via focus_out_event callback on compose window). */
9913         manage_window_focus_in(compose->window, NULL, NULL);
9914
9915         file_list = filesel_select_multiple_files_open(_("Select file"));
9916
9917         if (file_list) {
9918                 compose_attach_from_list(compose, file_list, TRUE);
9919                 g_list_free(file_list);
9920         }
9921 }
9922
9923 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9924 {
9925         Compose *compose = (Compose *)data;
9926         GList *file_list;
9927         gint files_inserted = 0;
9928
9929         file_list = filesel_select_multiple_files_open(_("Select file"));
9930
9931         if (file_list) {
9932                 GList *tmp;
9933
9934                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9935                         gchar *file = (gchar *) tmp->data;
9936                         gchar *filedup = g_strdup(file);
9937                         gchar *shortfile = g_path_get_basename(filedup);
9938                         ComposeInsertResult res;
9939                         /* insert the file if the file is short or if the user confirmed that
9940                            he/she wants to insert the large file */
9941                         res = compose_insert_file(compose, file);
9942                         if (res == COMPOSE_INSERT_READ_ERROR) {
9943                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9944                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9945                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9946                                                         "for the current encoding, insertion may be incorrect."),
9947                                                         shortfile);
9948                         } else if (res == COMPOSE_INSERT_SUCCESS)
9949                                 files_inserted++;
9950
9951                         g_free(shortfile);
9952                         g_free(filedup);
9953                         g_free(file);
9954                 }
9955                 g_list_free(file_list);
9956         }
9957
9958 #ifdef USE_ENCHANT      
9959         if (files_inserted > 0 && compose->gtkaspell && 
9960             compose->gtkaspell->check_while_typing)
9961                 gtkaspell_highlight_all(compose->gtkaspell);
9962 #endif
9963 }
9964
9965 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9966 {
9967         Compose *compose = (Compose *)data;
9968
9969         compose_insert_sig(compose, FALSE);
9970 }
9971
9972 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9973                               gpointer data)
9974 {
9975         gint x, y;
9976         Compose *compose = (Compose *)data;
9977
9978         gtkut_widget_get_uposition(widget, &x, &y);
9979         if (!compose->batch) {
9980                 prefs_common.compose_x = x;
9981                 prefs_common.compose_y = y;
9982         }
9983         if (compose->sending || compose->updating)
9984                 return TRUE;
9985         compose_close_cb(NULL, compose);
9986         return TRUE;
9987 }
9988
9989 void compose_close_toolbar(Compose *compose)
9990 {
9991         compose_close_cb(NULL, compose);
9992 }
9993
9994 static void compose_close_cb(GtkAction *action, gpointer data)
9995 {
9996         Compose *compose = (Compose *)data;
9997         AlertValue val;
9998
9999 #ifdef G_OS_UNIX
10000         if (compose->exteditor_tag != -1) {
10001                 if (!compose_ext_editor_kill(compose))
10002                         return;
10003         }
10004 #endif
10005
10006         if (compose->modified) {
10007                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10008                 if (!g_mutex_trylock(compose->mutex)) {
10009                         /* we don't want to lock the mutex once it's available,
10010                          * because as the only other part of compose.c locking
10011                          * it is compose_close - which means once unlocked,
10012                          * the compose struct will be freed */
10013                         debug_print("couldn't lock mutex, probably sending\n");
10014                         return;
10015                 }
10016                 if (!reedit) {
10017                         val = alertpanel(_("Discard message"),
10018                                  _("This message has been modified. Discard it?"),
10019                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10020                 } else {
10021                         val = alertpanel(_("Save changes"),
10022                                  _("This message has been modified. Save the latest changes?"),
10023                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10024                 }
10025                 g_mutex_unlock(compose->mutex);
10026                 switch (val) {
10027                 case G_ALERTDEFAULT:
10028                         if (prefs_common.autosave && !reedit)
10029                                 compose_remove_draft(compose);                  
10030                         break;
10031                 case G_ALERTALTERNATE:
10032                         compose_draft(data, COMPOSE_QUIT_EDITING);
10033                         return;
10034                 default:
10035                         return;
10036                 }
10037         }
10038
10039         compose_close(compose);
10040 }
10041
10042 static void compose_print_cb(GtkAction *action, gpointer data)
10043 {
10044         Compose *compose = (Compose *) data;
10045
10046         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10047         if (compose->targetinfo)
10048                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10049 }
10050
10051 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10052 {
10053         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10054         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10055         Compose *compose = (Compose *) data;
10056
10057         if (active)
10058                 compose->out_encoding = (CharSet)value;
10059 }
10060
10061 static void compose_address_cb(GtkAction *action, gpointer data)
10062 {
10063         Compose *compose = (Compose *)data;
10064
10065 #ifndef USE_NEW_ADDRBOOK
10066         addressbook_open(compose);
10067 #else
10068         GError* error = NULL;
10069         addressbook_connect_signals(compose);
10070         addressbook_dbus_open(TRUE, &error);
10071         if (error) {
10072                 g_warning("%s", error->message);
10073                 g_error_free(error);
10074         }
10075 #endif
10076 }
10077
10078 static void about_show_cb(GtkAction *action, gpointer data)
10079 {
10080         about_show();
10081 }
10082
10083 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10084 {
10085         Compose *compose = (Compose *)data;
10086         Template *tmpl;
10087         gchar *msg;
10088         AlertValue val;
10089
10090         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10091         cm_return_if_fail(tmpl != NULL);
10092
10093         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10094                               tmpl->name);
10095         val = alertpanel(_("Apply template"), msg,
10096                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10097         g_free(msg);
10098
10099         if (val == G_ALERTDEFAULT)
10100                 compose_template_apply(compose, tmpl, TRUE);
10101         else if (val == G_ALERTALTERNATE)
10102                 compose_template_apply(compose, tmpl, FALSE);
10103 }
10104
10105 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10106 {
10107         Compose *compose = (Compose *)data;
10108
10109         compose_exec_ext_editor(compose);
10110 }
10111
10112 static void compose_undo_cb(GtkAction *action, gpointer data)
10113 {
10114         Compose *compose = (Compose *)data;
10115         gboolean prev_autowrap = compose->autowrap;
10116
10117         compose->autowrap = FALSE;
10118         undo_undo(compose->undostruct);
10119         compose->autowrap = prev_autowrap;
10120 }
10121
10122 static void compose_redo_cb(GtkAction *action, gpointer data)
10123 {
10124         Compose *compose = (Compose *)data;
10125         gboolean prev_autowrap = compose->autowrap;
10126         
10127         compose->autowrap = FALSE;
10128         undo_redo(compose->undostruct);
10129         compose->autowrap = prev_autowrap;
10130 }
10131
10132 static void entry_cut_clipboard(GtkWidget *entry)
10133 {
10134         if (GTK_IS_EDITABLE(entry))
10135                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10136         else if (GTK_IS_TEXT_VIEW(entry))
10137                 gtk_text_buffer_cut_clipboard(
10138                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10139                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10140                         TRUE);
10141 }
10142
10143 static void entry_copy_clipboard(GtkWidget *entry)
10144 {
10145         if (GTK_IS_EDITABLE(entry))
10146                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10147         else if (GTK_IS_TEXT_VIEW(entry))
10148                 gtk_text_buffer_copy_clipboard(
10149                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10150                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10151 }
10152
10153 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10154                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10155 {
10156         if (GTK_IS_TEXT_VIEW(entry)) {
10157                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10158                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10159                 GtkTextIter start_iter, end_iter;
10160                 gint start, end;
10161                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10162
10163                 if (contents == NULL)
10164                         return;
10165         
10166                 /* we shouldn't delete the selection when middle-click-pasting, or we
10167                  * can't mid-click-paste our own selection */
10168                 if (clip != GDK_SELECTION_PRIMARY) {
10169                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10170                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10171                 }
10172                 
10173                 if (insert_place == NULL) {
10174                         /* if insert_place isn't specified, insert at the cursor.
10175                          * used for Ctrl-V pasting */
10176                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10177                         start = gtk_text_iter_get_offset(&start_iter);
10178                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10179                 } else {
10180                         /* if insert_place is specified, paste here.
10181                          * used for mid-click-pasting */
10182                         start = gtk_text_iter_get_offset(insert_place);
10183                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10184                         if (prefs_common.primary_paste_unselects)
10185                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10186                 }
10187                 
10188                 if (!wrap) {
10189                         /* paste unwrapped: mark the paste so it's not wrapped later */
10190                         end = start + strlen(contents);
10191                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10192                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10193                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10194                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10195                         /* rewrap paragraph now (after a mid-click-paste) */
10196                         mark_start = gtk_text_buffer_get_insert(buffer);
10197                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10198                         gtk_text_iter_backward_char(&start_iter);
10199                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10200                 }
10201         } else if (GTK_IS_EDITABLE(entry))
10202                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10203
10204         compose->modified = TRUE;
10205 }
10206
10207 static void entry_allsel(GtkWidget *entry)
10208 {
10209         if (GTK_IS_EDITABLE(entry))
10210                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10211         else if (GTK_IS_TEXT_VIEW(entry)) {
10212                 GtkTextIter startiter, enditer;
10213                 GtkTextBuffer *textbuf;
10214
10215                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10216                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10217                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10218
10219                 gtk_text_buffer_move_mark_by_name(textbuf, 
10220                         "selection_bound", &startiter);
10221                 gtk_text_buffer_move_mark_by_name(textbuf, 
10222                         "insert", &enditer);
10223         }
10224 }
10225
10226 static void compose_cut_cb(GtkAction *action, gpointer data)
10227 {
10228         Compose *compose = (Compose *)data;
10229         if (compose->focused_editable 
10230 #ifndef GENERIC_UMPC
10231             && gtk_widget_has_focus(compose->focused_editable)
10232 #endif
10233             )
10234                 entry_cut_clipboard(compose->focused_editable);
10235 }
10236
10237 static void compose_copy_cb(GtkAction *action, gpointer data)
10238 {
10239         Compose *compose = (Compose *)data;
10240         if (compose->focused_editable 
10241 #ifndef GENERIC_UMPC
10242             && gtk_widget_has_focus(compose->focused_editable)
10243 #endif
10244             )
10245                 entry_copy_clipboard(compose->focused_editable);
10246 }
10247
10248 static void compose_paste_cb(GtkAction *action, gpointer data)
10249 {
10250         Compose *compose = (Compose *)data;
10251         gint prev_autowrap;
10252         GtkTextBuffer *buffer;
10253         BLOCK_WRAP();
10254         if (compose->focused_editable &&
10255 #ifndef GENERIC_UMPC
10256             gtk_widget_has_focus(compose->focused_editable)
10257 #endif
10258                 )
10259                 entry_paste_clipboard(compose, compose->focused_editable, 
10260                                 prefs_common.linewrap_pastes,
10261                                 GDK_SELECTION_CLIPBOARD, NULL);
10262         UNBLOCK_WRAP();
10263
10264 #ifdef USE_ENCHANT
10265         if (
10266 #ifndef GENERIC_UMPC
10267                 gtk_widget_has_focus(compose->text) &&
10268 #endif
10269             compose->gtkaspell && 
10270             compose->gtkaspell->check_while_typing)
10271                 gtkaspell_highlight_all(compose->gtkaspell);
10272 #endif
10273 }
10274
10275 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10276 {
10277         Compose *compose = (Compose *)data;
10278         gint wrap_quote = prefs_common.linewrap_quote;
10279         if (compose->focused_editable 
10280 #ifndef GENERIC_UMPC
10281             && gtk_widget_has_focus(compose->focused_editable)
10282 #endif
10283             ) {
10284                 /* let text_insert() (called directly or at a later time
10285                  * after the gtk_editable_paste_clipboard) know that 
10286                  * text is to be inserted as a quotation. implemented
10287                  * by using a simple refcount... */
10288                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10289                                                 G_OBJECT(compose->focused_editable),
10290                                                 "paste_as_quotation"));
10291                 g_object_set_data(G_OBJECT(compose->focused_editable),
10292                                     "paste_as_quotation",
10293                                     GINT_TO_POINTER(paste_as_quotation + 1));
10294                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10295                 entry_paste_clipboard(compose, compose->focused_editable, 
10296                                 prefs_common.linewrap_pastes,
10297                                 GDK_SELECTION_CLIPBOARD, NULL);
10298                 prefs_common.linewrap_quote = wrap_quote;
10299         }
10300 }
10301
10302 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10303 {
10304         Compose *compose = (Compose *)data;
10305         gint prev_autowrap;
10306         GtkTextBuffer *buffer;
10307         BLOCK_WRAP();
10308         if (compose->focused_editable 
10309 #ifndef GENERIC_UMPC
10310             && gtk_widget_has_focus(compose->focused_editable)
10311 #endif
10312             )
10313                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10314                         GDK_SELECTION_CLIPBOARD, NULL);
10315         UNBLOCK_WRAP();
10316
10317 #ifdef USE_ENCHANT
10318         if (
10319 #ifndef GENERIC_UMPC
10320                 gtk_widget_has_focus(compose->text) &&
10321 #endif
10322             compose->gtkaspell && 
10323             compose->gtkaspell->check_while_typing)
10324                 gtkaspell_highlight_all(compose->gtkaspell);
10325 #endif
10326 }
10327
10328 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10329 {
10330         Compose *compose = (Compose *)data;
10331         gint prev_autowrap;
10332         GtkTextBuffer *buffer;
10333         BLOCK_WRAP();
10334         if (compose->focused_editable 
10335 #ifndef GENERIC_UMPC
10336             && gtk_widget_has_focus(compose->focused_editable)
10337 #endif
10338             )
10339                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10340                         GDK_SELECTION_CLIPBOARD, NULL);
10341         UNBLOCK_WRAP();
10342
10343 #ifdef USE_ENCHANT
10344         if (
10345 #ifndef GENERIC_UMPC
10346                 gtk_widget_has_focus(compose->text) &&
10347 #endif
10348             compose->gtkaspell &&
10349             compose->gtkaspell->check_while_typing)
10350                 gtkaspell_highlight_all(compose->gtkaspell);
10351 #endif
10352 }
10353
10354 static void compose_allsel_cb(GtkAction *action, gpointer data)
10355 {
10356         Compose *compose = (Compose *)data;
10357         if (compose->focused_editable 
10358 #ifndef GENERIC_UMPC
10359             && gtk_widget_has_focus(compose->focused_editable)
10360 #endif
10361             )
10362                 entry_allsel(compose->focused_editable);
10363 }
10364
10365 static void textview_move_beginning_of_line (GtkTextView *text)
10366 {
10367         GtkTextBuffer *buffer;
10368         GtkTextMark *mark;
10369         GtkTextIter ins;
10370
10371         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10372
10373         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10374         mark = gtk_text_buffer_get_insert(buffer);
10375         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10376         gtk_text_iter_set_line_offset(&ins, 0);
10377         gtk_text_buffer_place_cursor(buffer, &ins);
10378 }
10379
10380 static void textview_move_forward_character (GtkTextView *text)
10381 {
10382         GtkTextBuffer *buffer;
10383         GtkTextMark *mark;
10384         GtkTextIter ins;
10385
10386         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10387
10388         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10389         mark = gtk_text_buffer_get_insert(buffer);
10390         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10391         if (gtk_text_iter_forward_cursor_position(&ins))
10392                 gtk_text_buffer_place_cursor(buffer, &ins);
10393 }
10394
10395 static void textview_move_backward_character (GtkTextView *text)
10396 {
10397         GtkTextBuffer *buffer;
10398         GtkTextMark *mark;
10399         GtkTextIter ins;
10400
10401         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10402
10403         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10404         mark = gtk_text_buffer_get_insert(buffer);
10405         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10406         if (gtk_text_iter_backward_cursor_position(&ins))
10407                 gtk_text_buffer_place_cursor(buffer, &ins);
10408 }
10409
10410 static void textview_move_forward_word (GtkTextView *text)
10411 {
10412         GtkTextBuffer *buffer;
10413         GtkTextMark *mark;
10414         GtkTextIter ins;
10415         gint count;
10416
10417         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10418
10419         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10420         mark = gtk_text_buffer_get_insert(buffer);
10421         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10422         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10423         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10424                 gtk_text_iter_backward_word_start(&ins);
10425                 gtk_text_buffer_place_cursor(buffer, &ins);
10426         }
10427 }
10428
10429 static void textview_move_backward_word (GtkTextView *text)
10430 {
10431         GtkTextBuffer *buffer;
10432         GtkTextMark *mark;
10433         GtkTextIter ins;
10434
10435         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10436
10437         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10438         mark = gtk_text_buffer_get_insert(buffer);
10439         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10440         if (gtk_text_iter_backward_word_starts(&ins, 1))
10441                 gtk_text_buffer_place_cursor(buffer, &ins);
10442 }
10443
10444 static void textview_move_end_of_line (GtkTextView *text)
10445 {
10446         GtkTextBuffer *buffer;
10447         GtkTextMark *mark;
10448         GtkTextIter ins;
10449
10450         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10451
10452         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10453         mark = gtk_text_buffer_get_insert(buffer);
10454         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10455         if (gtk_text_iter_forward_to_line_end(&ins))
10456                 gtk_text_buffer_place_cursor(buffer, &ins);
10457 }
10458
10459 static void textview_move_next_line (GtkTextView *text)
10460 {
10461         GtkTextBuffer *buffer;
10462         GtkTextMark *mark;
10463         GtkTextIter ins;
10464         gint offset;
10465
10466         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10467
10468         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10469         mark = gtk_text_buffer_get_insert(buffer);
10470         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10471         offset = gtk_text_iter_get_line_offset(&ins);
10472         if (gtk_text_iter_forward_line(&ins)) {
10473                 gtk_text_iter_set_line_offset(&ins, offset);
10474                 gtk_text_buffer_place_cursor(buffer, &ins);
10475         }
10476 }
10477
10478 static void textview_move_previous_line (GtkTextView *text)
10479 {
10480         GtkTextBuffer *buffer;
10481         GtkTextMark *mark;
10482         GtkTextIter ins;
10483         gint offset;
10484
10485         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10486
10487         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10488         mark = gtk_text_buffer_get_insert(buffer);
10489         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10490         offset = gtk_text_iter_get_line_offset(&ins);
10491         if (gtk_text_iter_backward_line(&ins)) {
10492                 gtk_text_iter_set_line_offset(&ins, offset);
10493                 gtk_text_buffer_place_cursor(buffer, &ins);
10494         }
10495 }
10496
10497 static void textview_delete_forward_character (GtkTextView *text)
10498 {
10499         GtkTextBuffer *buffer;
10500         GtkTextMark *mark;
10501         GtkTextIter ins, end_iter;
10502
10503         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10504
10505         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10506         mark = gtk_text_buffer_get_insert(buffer);
10507         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10508         end_iter = ins;
10509         if (gtk_text_iter_forward_char(&end_iter)) {
10510                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10511         }
10512 }
10513
10514 static void textview_delete_backward_character (GtkTextView *text)
10515 {
10516         GtkTextBuffer *buffer;
10517         GtkTextMark *mark;
10518         GtkTextIter ins, end_iter;
10519
10520         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10521
10522         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10523         mark = gtk_text_buffer_get_insert(buffer);
10524         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10525         end_iter = ins;
10526         if (gtk_text_iter_backward_char(&end_iter)) {
10527                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10528         }
10529 }
10530
10531 static void textview_delete_forward_word (GtkTextView *text)
10532 {
10533         GtkTextBuffer *buffer;
10534         GtkTextMark *mark;
10535         GtkTextIter ins, end_iter;
10536
10537         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10538
10539         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10540         mark = gtk_text_buffer_get_insert(buffer);
10541         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10542         end_iter = ins;
10543         if (gtk_text_iter_forward_word_end(&end_iter)) {
10544                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10545         }
10546 }
10547
10548 static void textview_delete_backward_word (GtkTextView *text)
10549 {
10550         GtkTextBuffer *buffer;
10551         GtkTextMark *mark;
10552         GtkTextIter ins, end_iter;
10553
10554         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10555
10556         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10557         mark = gtk_text_buffer_get_insert(buffer);
10558         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10559         end_iter = ins;
10560         if (gtk_text_iter_backward_word_start(&end_iter)) {
10561                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10562         }
10563 }
10564
10565 static void textview_delete_line (GtkTextView *text)
10566 {
10567         GtkTextBuffer *buffer;
10568         GtkTextMark *mark;
10569         GtkTextIter ins, start_iter, end_iter;
10570
10571         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10572
10573         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10574         mark = gtk_text_buffer_get_insert(buffer);
10575         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10576
10577         start_iter = ins;
10578         gtk_text_iter_set_line_offset(&start_iter, 0);
10579
10580         end_iter = ins;
10581         if (gtk_text_iter_ends_line(&end_iter)){
10582                 if (!gtk_text_iter_forward_char(&end_iter))
10583                         gtk_text_iter_backward_char(&start_iter);
10584         }
10585         else 
10586                 gtk_text_iter_forward_to_line_end(&end_iter);
10587         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10588 }
10589
10590 static void textview_delete_to_line_end (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_ends_line(&end_iter))
10603                 gtk_text_iter_forward_char(&end_iter);
10604         else
10605                 gtk_text_iter_forward_to_line_end(&end_iter);
10606         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10607 }
10608
10609 #define DO_ACTION(name, act) {                                          \
10610         if(!strcmp(name, a_name)) {                                     \
10611                 return act;                                             \
10612         }                                                               \
10613 }
10614 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10615 {
10616         const gchar *a_name = gtk_action_get_name(action);
10617         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10618         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10619         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10620         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10621         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10622         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10623         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10624         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10625         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10626         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10627         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10628         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10629         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10630         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10631         return -1;
10632 }
10633
10634 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10635 {
10636         Compose *compose = (Compose *)data;
10637         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10638         ComposeCallAdvancedAction action = -1;
10639         
10640         action = compose_call_advanced_action_from_path(gaction);
10641
10642         static struct {
10643                 void (*do_action) (GtkTextView *text);
10644         } action_table[] = {
10645                 {textview_move_beginning_of_line},
10646                 {textview_move_forward_character},
10647                 {textview_move_backward_character},
10648                 {textview_move_forward_word},
10649                 {textview_move_backward_word},
10650                 {textview_move_end_of_line},
10651                 {textview_move_next_line},
10652                 {textview_move_previous_line},
10653                 {textview_delete_forward_character},
10654                 {textview_delete_backward_character},
10655                 {textview_delete_forward_word},
10656                 {textview_delete_backward_word},
10657                 {textview_delete_line},
10658                 {textview_delete_to_line_end}
10659         };
10660
10661         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10662
10663         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10664             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10665                 if (action_table[action].do_action)
10666                         action_table[action].do_action(text);
10667                 else
10668                         g_warning("Not implemented yet.");
10669         }
10670 }
10671
10672 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10673 {
10674         GtkAllocation allocation;
10675         GtkWidget *parent;
10676         gchar *str = NULL;
10677         
10678         if (GTK_IS_EDITABLE(widget)) {
10679                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10680                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10681                         strlen(str));
10682                 g_free(str);
10683                 if ((parent = gtk_widget_get_parent(widget))
10684                  && (parent = gtk_widget_get_parent(parent))
10685                  && (parent = gtk_widget_get_parent(parent))) {
10686                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
10687                                 gtk_widget_get_allocation(widget, &allocation);
10688                                 gint y = allocation.y;
10689                                 gint height = allocation.height;
10690                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10691                                         (GTK_SCROLLED_WINDOW(parent));
10692
10693                                 gfloat value = gtk_adjustment_get_value(shown);
10694                                 gfloat upper = gtk_adjustment_get_upper(shown);
10695                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
10696                                 if (y < (int)value) {
10697                                         gtk_adjustment_set_value(shown, y - 1);
10698                                 }
10699                                 if ((y + height) > ((int)value + (int)page_size)) {
10700                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
10701                                                 gtk_adjustment_set_value(shown, 
10702                                                         y + height - (int)page_size - 1);
10703                                         } else {
10704                                                 gtk_adjustment_set_value(shown, 
10705                                                         (int)upper - (int)page_size - 1);
10706                                         }
10707                                 }
10708                         }
10709                 }
10710         }
10711
10712         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10713                 compose->focused_editable = widget;
10714         
10715 #ifdef GENERIC_UMPC
10716         if (GTK_IS_TEXT_VIEW(widget) 
10717             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10718                 g_object_ref(compose->notebook);
10719                 g_object_ref(compose->edit_vbox);
10720                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10721                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10722                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10723                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10724                 g_object_unref(compose->notebook);
10725                 g_object_unref(compose->edit_vbox);
10726                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10727                                         G_CALLBACK(compose_grab_focus_cb),
10728                                         compose);
10729                 gtk_widget_grab_focus(widget);
10730                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10731                                         G_CALLBACK(compose_grab_focus_cb),
10732                                         compose);
10733         } else if (!GTK_IS_TEXT_VIEW(widget) 
10734                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10735                 g_object_ref(compose->notebook);
10736                 g_object_ref(compose->edit_vbox);
10737                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10738                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10739                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10740                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10741                 g_object_unref(compose->notebook);
10742                 g_object_unref(compose->edit_vbox);
10743                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10744                                         G_CALLBACK(compose_grab_focus_cb),
10745                                         compose);
10746                 gtk_widget_grab_focus(widget);
10747                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10748                                         G_CALLBACK(compose_grab_focus_cb),
10749                                         compose);
10750         }
10751 #endif
10752 }
10753
10754 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10755 {
10756         compose->modified = TRUE;
10757 //      compose_beautify_paragraph(compose, NULL, TRUE);
10758 #ifndef GENERIC_UMPC
10759         compose_set_title(compose);
10760 #endif
10761 }
10762
10763 static void compose_wrap_cb(GtkAction *action, gpointer data)
10764 {
10765         Compose *compose = (Compose *)data;
10766         compose_beautify_paragraph(compose, NULL, TRUE);
10767 }
10768
10769 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10770 {
10771         Compose *compose = (Compose *)data;
10772         compose_wrap_all_full(compose, TRUE);
10773 }
10774
10775 static void compose_find_cb(GtkAction *action, gpointer data)
10776 {
10777         Compose *compose = (Compose *)data;
10778
10779         message_search_compose(compose);
10780 }
10781
10782 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10783                                          gpointer        data)
10784 {
10785         Compose *compose = (Compose *)data;
10786         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10787         if (compose->autowrap)
10788                 compose_wrap_all_full(compose, TRUE);
10789         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10790 }
10791
10792 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10793                                          gpointer        data)
10794 {
10795         Compose *compose = (Compose *)data;
10796         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10797 }
10798
10799 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10800 {
10801         Compose *compose = (Compose *)data;
10802
10803         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10804 }
10805
10806 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10807 {
10808         Compose *compose = (Compose *)data;
10809
10810         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10811 }
10812
10813 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10814 {
10815         g_free(compose->privacy_system);
10816
10817         compose->privacy_system = g_strdup(account->default_privacy_system);
10818         compose_update_privacy_system_menu_item(compose, warn);
10819 }
10820
10821 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10822 {
10823         Compose *compose = (Compose *)data;
10824
10825         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10826                 gtk_widget_show(compose->ruler_hbox);
10827                 prefs_common.show_ruler = TRUE;
10828         } else {
10829                 gtk_widget_hide(compose->ruler_hbox);
10830                 gtk_widget_queue_resize(compose->edit_vbox);
10831                 prefs_common.show_ruler = FALSE;
10832         }
10833 }
10834
10835 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10836                                              GdkDragContext     *context,
10837                                              gint                x,
10838                                              gint                y,
10839                                              GtkSelectionData   *data,
10840                                              guint               info,
10841                                              guint               time,
10842                                              gpointer            user_data)
10843 {
10844         Compose *compose = (Compose *)user_data;
10845         GList *list, *tmp;
10846         GdkAtom type;
10847
10848         type = gtk_selection_data_get_data_type(data);
10849         if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10850 #ifdef G_OS_WIN32
10851          || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10852 #endif
10853            ) && gtk_drag_get_source_widget(context) != 
10854                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10855                 list = uri_list_extract_filenames(
10856                         (const gchar *)gtk_selection_data_get_data(data));
10857                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10858                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10859                         compose_attach_append
10860                                 (compose, (const gchar *)tmp->data,
10861                                  utf8_filename, NULL, NULL);
10862                         g_free(utf8_filename);
10863                 }
10864                 if (list) compose_changed_cb(NULL, compose);
10865                 list_free_strings(list);
10866                 g_list_free(list);
10867         } else if (gtk_drag_get_source_widget(context) 
10868                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10869                 /* comes from our summaryview */
10870                 SummaryView * summaryview = NULL;
10871                 GSList * list = NULL, *cur = NULL;
10872                 
10873                 if (mainwindow_get_mainwindow())
10874                         summaryview = mainwindow_get_mainwindow()->summaryview;
10875                 
10876                 if (summaryview)
10877                         list = summary_get_selected_msg_list(summaryview);
10878                 
10879                 for (cur = list; cur; cur = cur->next) {
10880                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10881                         gchar *file = NULL;
10882                         if (msginfo)
10883                                 file = procmsg_get_message_file_full(msginfo, 
10884                                         TRUE, TRUE);
10885                         if (file) {
10886                                 compose_attach_append(compose, (const gchar *)file, 
10887                                         (const gchar *)file, "message/rfc822", NULL);
10888                                 g_free(file);
10889                         }
10890                 }
10891                 g_slist_free(list);
10892         }
10893 }
10894
10895 static gboolean compose_drag_drop(GtkWidget *widget,
10896                                   GdkDragContext *drag_context,
10897                                   gint x, gint y,
10898                                   guint time, gpointer user_data)
10899 {
10900         /* not handling this signal makes compose_insert_drag_received_cb
10901          * called twice */
10902         return TRUE;                                     
10903 }
10904
10905 static gboolean completion_set_focus_to_subject
10906                                         (GtkWidget    *widget,
10907                                          GdkEventKey  *event,
10908                                          Compose      *compose)
10909 {
10910         cm_return_val_if_fail(compose != NULL, FALSE);
10911
10912         /* make backtab move to subject field */
10913         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10914                 gtk_widget_grab_focus(compose->subject_entry);
10915                 return TRUE;
10916         }
10917         return FALSE;
10918 }
10919
10920 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10921                                              GdkDragContext     *drag_context,
10922                                              gint                x,
10923                                              gint                y,
10924                                              GtkSelectionData   *data,
10925                                              guint               info,
10926                                              guint               time,
10927                                              gpointer            user_data)
10928 {
10929         Compose *compose = (Compose *)user_data;
10930         GList *list, *tmp;
10931         GdkAtom type;
10932
10933         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10934          * does not work */
10935         type = gtk_selection_data_get_data_type(data);
10936 #ifndef G_OS_WIN32
10937         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10938 #else
10939         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10940 #endif
10941                 AlertValue val = G_ALERTDEFAULT;
10942                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10943
10944                 list = uri_list_extract_filenames(ddata);
10945                 if (list == NULL && strstr(ddata, "://")) {
10946                         /* Assume a list of no files, and data has ://, is a remote link */
10947                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
10948                         gchar *tmpfile = get_tmp_file();
10949                         str_write_to_file(tmpdata, tmpfile);
10950                         g_free(tmpdata);  
10951                         compose_insert_file(compose, tmpfile);
10952                         claws_unlink(tmpfile);
10953                         g_free(tmpfile);
10954                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10955                         compose_beautify_paragraph(compose, NULL, TRUE);
10956                         return;
10957                 }
10958                 switch (prefs_common.compose_dnd_mode) {
10959                         case COMPOSE_DND_ASK:
10960                                 val = alertpanel_full(_("Insert or attach?"),
10961                                          _("Do you want to insert the contents of the file(s) "
10962                                            "into the message body, or attach it to the email?"),
10963                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10964                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10965                                 break;
10966                         case COMPOSE_DND_INSERT:
10967                                 val = G_ALERTALTERNATE;
10968                                 break;
10969                         case COMPOSE_DND_ATTACH:
10970                                 val = G_ALERTOTHER;
10971                                 break;
10972                         default:
10973                                 /* unexpected case */
10974                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10975                 }
10976
10977                 if (val & G_ALERTDISABLE) {
10978                         val &= ~G_ALERTDISABLE;
10979                         /* remember what action to perform by default, only if we don't click Cancel */
10980                         if (val == G_ALERTALTERNATE)
10981                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10982                         else if (val == G_ALERTOTHER)
10983                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10984                 }
10985
10986                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10987                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10988                         list_free_strings(list);
10989                         g_list_free(list);
10990                         return;
10991                 } else if (val == G_ALERTOTHER) {
10992                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10993                         list_free_strings(list);
10994                         g_list_free(list);
10995                         return;
10996                 } 
10997
10998                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10999                         compose_insert_file(compose, (const gchar *)tmp->data);
11000                 }
11001                 list_free_strings(list);
11002                 g_list_free(list);
11003                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11004                 return;
11005         } else {
11006                 return;
11007         }
11008         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11009 }
11010
11011 static void compose_header_drag_received_cb (GtkWidget          *widget,
11012                                              GdkDragContext     *drag_context,
11013                                              gint                x,
11014                                              gint                y,
11015                                              GtkSelectionData   *data,
11016                                              guint               info,
11017                                              guint               time,
11018                                              gpointer            user_data)
11019 {
11020         GtkEditable *entry = (GtkEditable *)user_data;
11021         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11022
11023         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11024          * does not work */
11025
11026         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11027                 gchar *decoded=g_new(gchar, strlen(email));
11028                 int start = 0;
11029
11030                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11031                 gtk_editable_delete_text(entry, 0, -1);
11032                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11033                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11034                 g_free(decoded);
11035                 return;
11036         }
11037         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11038 }
11039
11040 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11041 {
11042         Compose *compose = (Compose *)data;
11043
11044         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11045                 compose->return_receipt = TRUE;
11046         else
11047                 compose->return_receipt = FALSE;
11048 }
11049
11050 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11051 {
11052         Compose *compose = (Compose *)data;
11053
11054         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11055                 compose->remove_references = TRUE;
11056         else
11057                 compose->remove_references = FALSE;
11058 }
11059
11060 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11061                                         ComposeHeaderEntry *headerentry)
11062 {
11063         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11064         return FALSE;
11065 }
11066
11067 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11068                                             GdkEventKey *event,
11069                                             ComposeHeaderEntry *headerentry)
11070 {
11071         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11072             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11073             !(event->state & GDK_MODIFIER_MASK) &&
11074             (event->keyval == GDK_KEY_BackSpace) &&
11075             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11076                 gtk_container_remove
11077                         (GTK_CONTAINER(headerentry->compose->header_table),
11078                          headerentry->combo);
11079                 gtk_container_remove
11080                         (GTK_CONTAINER(headerentry->compose->header_table),
11081                          headerentry->entry);
11082                 headerentry->compose->header_list =
11083                         g_slist_remove(headerentry->compose->header_list,
11084                                        headerentry);
11085                 g_free(headerentry);
11086         } else  if (event->keyval == GDK_KEY_Tab) {
11087                 if (headerentry->compose->header_last == headerentry) {
11088                         /* Override default next focus, and give it to subject_entry
11089                          * instead of notebook tabs
11090                          */
11091                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11092                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11093                         return TRUE;
11094                 }
11095         }
11096         return FALSE;
11097 }
11098
11099 static gboolean scroll_postpone(gpointer data)
11100 {
11101         Compose *compose = (Compose *)data;
11102
11103         cm_return_val_if_fail(!compose->batch, FALSE);
11104
11105         GTK_EVENTS_FLUSH();
11106         compose_show_first_last_header(compose, FALSE);
11107         return FALSE;
11108 }
11109
11110 static void compose_headerentry_changed_cb(GtkWidget *entry,
11111                                     ComposeHeaderEntry *headerentry)
11112 {
11113         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11114                 compose_create_header_entry(headerentry->compose);
11115                 g_signal_handlers_disconnect_matched
11116                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11117                          0, 0, NULL, NULL, headerentry);
11118
11119                 if (!headerentry->compose->batch)
11120                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11121         }
11122 }
11123
11124 static gboolean compose_defer_auto_save_draft(Compose *compose)
11125 {
11126         compose->draft_timeout_tag = -1;
11127         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11128         return FALSE;
11129 }
11130
11131 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11132 {
11133         GtkAdjustment *vadj;
11134
11135         cm_return_if_fail(compose);
11136         cm_return_if_fail(!compose->batch);
11137         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11138         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11139         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11140                                 gtk_widget_get_parent(compose->header_table)));
11141         gtk_adjustment_set_value(vadj, (show_first ?
11142                                 gtk_adjustment_get_lower(vadj) :
11143                                 (gtk_adjustment_get_upper(vadj) -
11144                                 gtk_adjustment_get_page_size(vadj))));
11145         gtk_adjustment_changed(vadj);
11146 }
11147
11148 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11149                           const gchar *text, gint len, Compose *compose)
11150 {
11151         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11152                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11153         GtkTextMark *mark;
11154
11155         cm_return_if_fail(text != NULL);
11156
11157         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11158                                         G_CALLBACK(text_inserted),
11159                                         compose);
11160         if (paste_as_quotation) {
11161                 gchar *new_text;
11162                 const gchar *qmark;
11163                 guint pos = 0;
11164                 GtkTextIter start_iter;
11165
11166                 if (len < 0)
11167                         len = strlen(text);
11168
11169                 new_text = g_strndup(text, len);
11170
11171                 qmark = compose_quote_char_from_context(compose);
11172
11173                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11174                 gtk_text_buffer_place_cursor(buffer, iter);
11175
11176                 pos = gtk_text_iter_get_offset(iter);
11177
11178                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11179                                                   _("Quote format error at line %d."));
11180                 quote_fmt_reset_vartable();
11181                 g_free(new_text);
11182                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11183                                   GINT_TO_POINTER(paste_as_quotation - 1));
11184                                   
11185                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11186                 gtk_text_buffer_place_cursor(buffer, iter);
11187                 gtk_text_buffer_delete_mark(buffer, mark);
11188
11189                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11190                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11191                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11192                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11193                 gtk_text_buffer_delete_mark(buffer, mark);
11194         } else {
11195                 if (strcmp(text, "\n") || compose->automatic_break
11196                 || gtk_text_iter_starts_line(iter)) {
11197                         GtkTextIter before_ins;
11198                         gtk_text_buffer_insert(buffer, iter, text, len);
11199                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11200                                 before_ins = *iter; 
11201                                 gtk_text_iter_backward_chars(&before_ins, len);
11202                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11203                         }
11204                 } else {
11205                         /* check if the preceding is just whitespace or quote */
11206                         GtkTextIter start_line;
11207                         gchar *tmp = NULL, *quote = NULL;
11208                         gint quote_len = 0, is_normal = 0;
11209                         start_line = *iter;
11210                         gtk_text_iter_set_line_offset(&start_line, 0); 
11211                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11212                         g_strstrip(tmp);
11213
11214                         if (*tmp == '\0') {
11215                                 is_normal = 1;
11216                         } else {
11217                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11218                                 if (quote)
11219                                         is_normal = 1;
11220                                 g_free(quote);
11221                         }
11222                         g_free(tmp);
11223                         
11224                         if (is_normal) {
11225                                 gtk_text_buffer_insert(buffer, iter, text, len);
11226                         } else {
11227                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11228                                         iter, text, len, "no_join", NULL);
11229                         }
11230                 }
11231         }
11232         
11233         if (!paste_as_quotation) {
11234                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11235                 compose_beautify_paragraph(compose, iter, FALSE);
11236                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11237                 gtk_text_buffer_delete_mark(buffer, mark);
11238         }
11239
11240         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11241                                           G_CALLBACK(text_inserted),
11242                                           compose);
11243         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11244
11245         if (prefs_common.autosave && 
11246             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11247             compose->draft_timeout_tag != -2 /* disabled while loading */)
11248                 compose->draft_timeout_tag = g_timeout_add
11249                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11250 }
11251
11252 #if USE_ENCHANT
11253 static void compose_check_all(GtkAction *action, gpointer data)
11254 {
11255         Compose *compose = (Compose *)data;
11256         if (!compose->gtkaspell)
11257                 return;
11258                 
11259         if (gtk_widget_has_focus(compose->subject_entry))
11260                 claws_spell_entry_check_all(
11261                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11262         else
11263                 gtkaspell_check_all(compose->gtkaspell);
11264 }
11265
11266 static void compose_highlight_all(GtkAction *action, gpointer data)
11267 {
11268         Compose *compose = (Compose *)data;
11269         if (compose->gtkaspell) {
11270                 claws_spell_entry_recheck_all(
11271                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11272                 gtkaspell_highlight_all(compose->gtkaspell);
11273         }
11274 }
11275
11276 static void compose_check_backwards(GtkAction *action, gpointer data)
11277 {
11278         Compose *compose = (Compose *)data;
11279         if (!compose->gtkaspell) {
11280                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11281                 return;
11282         }
11283
11284         if (gtk_widget_has_focus(compose->subject_entry))
11285                 claws_spell_entry_check_backwards(
11286                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11287         else
11288                 gtkaspell_check_backwards(compose->gtkaspell);
11289 }
11290
11291 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11292 {
11293         Compose *compose = (Compose *)data;
11294         if (!compose->gtkaspell) {
11295                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11296                 return;
11297         }
11298
11299         if (gtk_widget_has_focus(compose->subject_entry))
11300                 claws_spell_entry_check_forwards_go(
11301                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11302         else
11303                 gtkaspell_check_forwards_go(compose->gtkaspell);
11304 }
11305 #endif
11306
11307 /*!
11308  *\brief        Guess originating forward account from MsgInfo and several 
11309  *              "common preference" settings. Return NULL if no guess. 
11310  */
11311 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11312 {
11313         PrefsAccount *account = NULL;
11314         
11315         cm_return_val_if_fail(msginfo, NULL);
11316         cm_return_val_if_fail(msginfo->folder, NULL);
11317         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11318
11319         if (msginfo->folder->prefs->enable_default_account)
11320                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11321                 
11322         if (!account) 
11323                 account = msginfo->folder->folder->account;
11324                 
11325         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11326                 gchar *to;
11327                 Xstrdup_a(to, msginfo->to, return NULL);
11328                 extract_address(to);
11329                 account = account_find_from_address(to, FALSE);
11330         }
11331
11332         if (!account && prefs_common.forward_account_autosel) {
11333                 gchar cc[BUFFSIZE];
11334                 if (!procheader_get_header_from_msginfo
11335                         (msginfo, cc,sizeof cc , "Cc:")) { 
11336                         gchar *buf = cc + strlen("Cc:");
11337                         extract_address(buf);
11338                         account = account_find_from_address(buf, FALSE);
11339                 }
11340         }
11341         
11342         if (!account && prefs_common.forward_account_autosel) {
11343                 gchar deliveredto[BUFFSIZE];
11344                 if (!procheader_get_header_from_msginfo
11345                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11346                         gchar *buf = deliveredto + strlen("Delivered-To:");
11347                         extract_address(buf);
11348                         account = account_find_from_address(buf, FALSE);
11349                 }
11350         }
11351         
11352         return account;
11353 }
11354
11355 gboolean compose_close(Compose *compose)
11356 {
11357         gint x, y;
11358
11359         if (!g_mutex_trylock(compose->mutex)) {
11360                 /* we have to wait for the (possibly deferred by auto-save)
11361                  * drafting to be done, before destroying the compose under
11362                  * it. */
11363                 debug_print("waiting for drafting to finish...\n");
11364                 compose_allow_user_actions(compose, FALSE);
11365                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11366                 return FALSE;
11367         }
11368         cm_return_val_if_fail(compose, FALSE);
11369         gtkut_widget_get_uposition(compose->window, &x, &y);
11370         if (!compose->batch) {
11371                 prefs_common.compose_x = x;
11372                 prefs_common.compose_y = y;
11373         }
11374         g_mutex_unlock(compose->mutex);
11375         compose_destroy(compose);
11376         return FALSE;
11377 }
11378
11379 /**
11380  * Add entry field for each address in list.
11381  * \param compose     E-Mail composition object.
11382  * \param listAddress List of (formatted) E-Mail addresses.
11383  */
11384 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11385         GList *node;
11386         gchar *addr;
11387         node = listAddress;
11388         while( node ) {
11389                 addr = ( gchar * ) node->data;
11390                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11391                 node = g_list_next( node );
11392         }
11393 }
11394
11395 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11396                                     guint action, gboolean opening_multiple)
11397 {
11398         gchar *body = NULL;
11399         GSList *new_msglist = NULL;
11400         MsgInfo *tmp_msginfo = NULL;
11401         gboolean originally_enc = FALSE;
11402         gboolean originally_sig = FALSE;
11403         Compose *compose = NULL;
11404         gchar *s_system = NULL;
11405
11406         cm_return_if_fail(msgview != NULL);
11407
11408         cm_return_if_fail(msginfo_list != NULL);
11409
11410         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11411                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11412                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11413
11414                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11415                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11416                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11417                                                 orig_msginfo, mimeinfo);
11418                         if (tmp_msginfo != NULL) {
11419                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11420
11421                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11422                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11423                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11424
11425                                 tmp_msginfo->folder = orig_msginfo->folder;
11426                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11427                                 if (orig_msginfo->tags) {
11428                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11429                                         tmp_msginfo->folder->tags_dirty = TRUE;
11430                                 }
11431                         }
11432                 }
11433         }
11434
11435         if (!opening_multiple)
11436                 body = messageview_get_selection(msgview);
11437
11438         if (new_msglist) {
11439                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11440                 procmsg_msginfo_free(tmp_msginfo);
11441                 g_slist_free(new_msglist);
11442         } else
11443                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11444
11445         if (compose && originally_enc) {
11446                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11447         }
11448
11449         if (compose && originally_sig && compose->account->default_sign_reply) {
11450                 compose_force_signing(compose, compose->account, s_system);
11451         }
11452         g_free(s_system);
11453         g_free(body);
11454         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11455 }
11456
11457 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11458                                     guint action)
11459 {
11460         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11461         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11462                 GSList *cur = msginfo_list;
11463                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11464                                                "messages. Opening the windows "
11465                                                "could take some time. Do you "
11466                                                "want to continue?"), 
11467                                                g_slist_length(msginfo_list));
11468                 if (g_slist_length(msginfo_list) > 9
11469                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11470                     != G_ALERTALTERNATE) {
11471                         g_free(msg);
11472                         return;
11473                 }
11474                 g_free(msg);
11475                 /* We'll open multiple compose windows */
11476                 /* let the WM place the next windows */
11477                 compose_force_window_origin = FALSE;
11478                 for (; cur; cur = cur->next) {
11479                         GSList tmplist;
11480                         tmplist.data = cur->data;
11481                         tmplist.next = NULL;
11482                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11483                 }
11484                 compose_force_window_origin = TRUE;
11485         } else {
11486                 /* forwarding multiple mails as attachments is done via a
11487                  * single compose window */
11488                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11489         }
11490 }
11491
11492 void compose_check_for_email_account(Compose *compose)
11493 {
11494         PrefsAccount *ac = NULL, *curr = NULL;
11495         GList *list;
11496         
11497         if (!compose)
11498                 return;
11499
11500         if (compose->account && compose->account->protocol == A_NNTP) {
11501                 ac = account_get_cur_account();
11502                 if (ac->protocol == A_NNTP) {
11503                         list = account_get_list();
11504                         
11505                         for( ; list != NULL ; list = g_list_next(list)) {
11506                                 curr = (PrefsAccount *) list->data;
11507                                 if (curr->protocol != A_NNTP) {
11508                                         ac = curr;
11509                                         break;
11510                                 }
11511                         }
11512                 }
11513                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11514                                         ac->account_id); 
11515         }
11516 }
11517
11518 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11519                                 const gchar *address)
11520 {
11521         GSList *msginfo_list = NULL;
11522         gchar *body =  messageview_get_selection(msgview);
11523         Compose *compose;
11524         
11525         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11526         
11527         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11528         compose_check_for_email_account(compose);
11529         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11530         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11531         compose_reply_set_subject(compose, msginfo);
11532
11533         g_free(body);
11534         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11535 }
11536
11537 void compose_set_position(Compose *compose, gint pos)
11538 {
11539         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11540
11541         gtkut_text_view_set_position(text, pos);
11542 }
11543
11544 gboolean compose_search_string(Compose *compose,
11545                                 const gchar *str, gboolean case_sens)
11546 {
11547         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11548
11549         return gtkut_text_view_search_string(text, str, case_sens);
11550 }
11551
11552 gboolean compose_search_string_backward(Compose *compose,
11553                                 const gchar *str, gboolean case_sens)
11554 {
11555         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11556
11557         return gtkut_text_view_search_string_backward(text, str, case_sens);
11558 }
11559
11560 /* allocate a msginfo structure and populate its data from a compose data structure */
11561 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11562 {
11563         MsgInfo *newmsginfo;
11564         GSList *list;
11565         gchar buf[BUFFSIZE];
11566
11567         cm_return_val_if_fail( compose != NULL, NULL );
11568
11569         newmsginfo = procmsg_msginfo_new();
11570
11571         /* date is now */
11572         get_rfc822_date(buf, sizeof(buf));
11573         newmsginfo->date = g_strdup(buf);
11574
11575         /* from */
11576         if (compose->from_name) {
11577                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11578                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11579         }
11580
11581         /* subject */
11582         if (compose->subject_entry)
11583                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11584
11585         /* to, cc, reply-to, newsgroups */
11586         for (list = compose->header_list; list; list = list->next) {
11587                 gchar *header = gtk_editable_get_chars(
11588                                                                 GTK_EDITABLE(
11589                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11590                 gchar *entry = gtk_editable_get_chars(
11591                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11592
11593                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11594                         if ( newmsginfo->to == NULL ) {
11595                                 newmsginfo->to = g_strdup(entry);
11596                         } else if (entry && *entry) {
11597                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11598                                 g_free(newmsginfo->to);
11599                                 newmsginfo->to = tmp;
11600                         }
11601                 } else
11602                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11603                         if ( newmsginfo->cc == NULL ) {
11604                                 newmsginfo->cc = g_strdup(entry);
11605                         } else if (entry && *entry) {
11606                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11607                                 g_free(newmsginfo->cc);
11608                                 newmsginfo->cc = tmp;
11609                         }
11610                 } else
11611                 if ( strcasecmp(header,
11612                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11613                         if ( newmsginfo->newsgroups == NULL ) {
11614                                 newmsginfo->newsgroups = g_strdup(entry);
11615                         } else if (entry && *entry) {
11616                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11617                                 g_free(newmsginfo->newsgroups);
11618                                 newmsginfo->newsgroups = tmp;
11619                         }
11620                 }
11621
11622                 g_free(header);
11623                 g_free(entry);  
11624         }
11625
11626         /* other data is unset */
11627
11628         return newmsginfo;
11629 }
11630
11631 #ifdef USE_ENCHANT
11632 /* update compose's dictionaries from folder dict settings */
11633 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11634                                                 FolderItem *folder_item)
11635 {
11636         cm_return_if_fail(compose != NULL);
11637
11638         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11639                 FolderItemPrefs *prefs = folder_item->prefs;
11640
11641                 if (prefs->enable_default_dictionary)
11642                         gtkaspell_change_dict(compose->gtkaspell,
11643                                         prefs->default_dictionary, FALSE);
11644                 if (folder_item->prefs->enable_default_alt_dictionary)
11645                         gtkaspell_change_alt_dict(compose->gtkaspell,
11646                                         prefs->default_alt_dictionary);
11647                 if (prefs->enable_default_dictionary
11648                         || prefs->enable_default_alt_dictionary)
11649                         compose_spell_menu_changed(compose);
11650         }
11651 }
11652 #endif
11653
11654 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11655 {
11656         Compose *compose = (Compose *)data;
11657
11658         cm_return_if_fail(compose != NULL);
11659
11660         gtk_widget_grab_focus(compose->text);
11661 }
11662
11663 /*
11664  * End of Source.
11665  */