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