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