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