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