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