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