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