2010-09-12 [pawel] 3.7.6cvs35
[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 || (compose->followup_to && 
3198                         !strncmp(compose->followup_to, "poster", 6)))
3199                         compose_entry_append
3200                                 (compose, 
3201                                  (compose->replyto ? compose->replyto :
3202                                         msginfo->from ? msginfo->from : ""),
3203                                  COMPOSE_TO, PREF_NONE);
3204                                  
3205                 else if (followup_and_reply_to || to_all) {
3206                         compose_entry_append
3207                                 (compose,
3208                                  (compose->replyto ? compose->replyto :
3209                                  msginfo->from ? msginfo->from : ""),
3210                                  COMPOSE_TO, PREF_NONE);                                
3211                 
3212                         compose_entry_append
3213                                 (compose,
3214                                  compose->followup_to ? compose->followup_to :
3215                                  compose->newsgroups ? compose->newsgroups : "",
3216                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3217                 } else if (reply_to_ml) {
3218                         compose_check_for_email_account(compose);
3219                         compose_entry_append(compose,
3220                                    compose->ml_post, COMPOSE_TO, PREF_ML); 
3221                 } else 
3222                         compose_entry_append
3223                                 (compose,
3224                                  compose->followup_to ? compose->followup_to :
3225                                  compose->newsgroups ? compose->newsgroups : "",
3226                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3227         }
3228         compose_reply_set_subject(compose, msginfo);
3229
3230         if (to_ml && compose->ml_post) return;
3231         if (!to_all) return;
3232
3233         if (compose->replyto) {
3234                 Xstrdup_a(replyto, compose->replyto, return);
3235                 extract_address(replyto);
3236         }
3237         if (msginfo->from) {
3238                 Xstrdup_a(from, msginfo->from, return);
3239                 extract_address(from);
3240         }
3241
3242         if (replyto && from)
3243                 cc_list = address_list_append_with_comments(cc_list, from);
3244         if (to_all && msginfo->folder && 
3245             msginfo->folder->prefs->enable_default_reply_to)
3246                 cc_list = address_list_append_with_comments(cc_list,
3247                                 msginfo->folder->prefs->default_reply_to);
3248         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3249         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3250
3251         ac_email = g_utf8_strdown(compose->account->address, -1);
3252
3253         if (cc_list) {
3254                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3255                         gchar *addr = g_utf8_strdown(cur->data, -1);
3256                         extract_address(addr);
3257                 
3258                         if (strcmp(ac_email, addr))
3259                                 compose_entry_append(compose, (gchar *)cur->data,
3260                                                      COMPOSE_CC, PREF_NONE);
3261                         else
3262                                 debug_print("Cc address same as compose account's, ignoring\n");
3263
3264                         g_free(addr);
3265                 }
3266                 
3267                 slist_free_strings(cc_list);
3268                 g_slist_free(cc_list);
3269         }
3270         
3271         g_free(ac_email);
3272 }
3273
3274 #define SET_ENTRY(entry, str) \
3275 { \
3276         if (str && *str) \
3277                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3278 }
3279
3280 #define SET_ADDRESS(type, str) \
3281 { \
3282         if (str && *str) \
3283                 compose_entry_append(compose, str, type, PREF_NONE); \
3284 }
3285
3286 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3287 {
3288         cm_return_if_fail(msginfo != NULL);
3289
3290         SET_ENTRY(subject_entry, msginfo->subject);
3291         SET_ENTRY(from_name, msginfo->from);
3292         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3293         SET_ADDRESS(COMPOSE_CC, compose->cc);
3294         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3295         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3296         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3297         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3298
3299         compose_update_priority_menu_item(compose);
3300         compose_update_privacy_system_menu_item(compose, FALSE);
3301         compose_show_first_last_header(compose, TRUE);
3302 }
3303
3304 #undef SET_ENTRY
3305 #undef SET_ADDRESS
3306
3307 static void compose_insert_sig(Compose *compose, gboolean replace)
3308 {
3309         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3310         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3311         GtkTextMark *mark;
3312         GtkTextIter iter, iter_end;
3313         gint cur_pos, ins_pos;
3314         gboolean prev_autowrap;
3315         gboolean found = FALSE;
3316         gboolean exists = FALSE;
3317         
3318         cm_return_if_fail(compose->account != NULL);
3319
3320         BLOCK_WRAP();
3321
3322         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3323                                         G_CALLBACK(compose_changed_cb),
3324                                         compose);
3325         
3326         mark = gtk_text_buffer_get_insert(buffer);
3327         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3328         cur_pos = gtk_text_iter_get_offset (&iter);
3329         ins_pos = cur_pos;
3330
3331         gtk_text_buffer_get_end_iter(buffer, &iter);
3332
3333         exists = (compose->sig_str != NULL);
3334
3335         if (replace) {
3336                 GtkTextIter first_iter, start_iter, end_iter;
3337
3338                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3339
3340                 if (!exists || compose->sig_str[0] == '\0')
3341                         found = FALSE;
3342                 else
3343                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3344                                         compose->signature_tag);
3345
3346                 if (found) {
3347                         /* include previous \n\n */
3348                         gtk_text_iter_backward_chars(&first_iter, 1);
3349                         start_iter = first_iter;
3350                         end_iter = first_iter;
3351                         /* skip re-start */
3352                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3353                                         compose->signature_tag);
3354                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3355                                         compose->signature_tag);
3356                         if (found) {
3357                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3358                                 iter = start_iter;
3359                         }
3360                 } 
3361         } 
3362
3363         g_free(compose->sig_str);
3364         compose->sig_str = account_get_signature_str(compose->account);
3365
3366         cur_pos = gtk_text_iter_get_offset(&iter);
3367
3368         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3369                 g_free(compose->sig_str);
3370                 compose->sig_str = NULL;
3371         } else {
3372                 if (compose->sig_inserted == FALSE)
3373                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3374                 compose->sig_inserted = TRUE;
3375
3376                 cur_pos = gtk_text_iter_get_offset(&iter);
3377                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3378                 /* remove \n\n */
3379                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3380                 gtk_text_iter_forward_chars(&iter, 1);
3381                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3382                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3383
3384                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3385                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3386         }
3387
3388         /* put the cursor where it should be 
3389          * either where the quote_fmt says, either where it was */
3390         if (compose->set_cursor_pos < 0)
3391                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3392         else
3393                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3394                         compose->set_cursor_pos);
3395         
3396         compose->set_cursor_pos = -1;
3397         gtk_text_buffer_place_cursor(buffer, &iter);
3398         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3399                                         G_CALLBACK(compose_changed_cb),
3400                                         compose);
3401                 
3402         UNBLOCK_WRAP();
3403 }
3404
3405 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3406 {
3407         GtkTextView *text;
3408         GtkTextBuffer *buffer;
3409         GtkTextMark *mark;
3410         GtkTextIter iter;
3411         const gchar *cur_encoding;
3412         gchar buf[BUFFSIZE];
3413         gint len;
3414         FILE *fp;
3415         gboolean prev_autowrap;
3416         gboolean badtxt = FALSE;
3417         struct stat file_stat;
3418         int ret;
3419
3420         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3421
3422         /* get the size of the file we are about to insert */
3423         ret = g_stat(file, &file_stat);
3424         if (ret != 0) {
3425                 gchar *shortfile = g_path_get_basename(file);
3426                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3427                 g_free(shortfile);
3428                 return COMPOSE_INSERT_NO_FILE;
3429         } else if (prefs_common.warn_large_insert == TRUE) {
3430
3431                 /* ask user for confirmation if the file is large */
3432                 if (prefs_common.warn_large_insert_size < 0 ||
3433                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3434                         AlertValue aval;
3435                         gchar *msg;
3436
3437                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3438                                                 "in the message body. Are you sure you want to do that?"),
3439                                                 to_human_readable(file_stat.st_size));
3440                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3441                                         _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3442                         g_free(msg);
3443
3444                         /* do we ask for confirmation next time? */
3445                         if (aval & G_ALERTDISABLE) {
3446                                 /* no confirmation next time, disable feature in preferences */
3447                                 aval &= ~G_ALERTDISABLE;
3448                                 prefs_common.warn_large_insert = FALSE;
3449                         }
3450
3451                         /* abort file insertion if user canceled action */
3452                         if (aval != G_ALERTALTERNATE) {
3453                                 return COMPOSE_INSERT_NO_FILE;
3454                         }
3455                 }
3456         }
3457
3458
3459         if ((fp = g_fopen(file, "rb")) == NULL) {
3460                 FILE_OP_ERROR(file, "fopen");
3461                 return COMPOSE_INSERT_READ_ERROR;
3462         }
3463
3464         prev_autowrap = compose->autowrap;
3465         compose->autowrap = FALSE;
3466
3467         text = GTK_TEXT_VIEW(compose->text);
3468         buffer = gtk_text_view_get_buffer(text);
3469         mark = gtk_text_buffer_get_insert(buffer);
3470         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3471
3472         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3473                                         G_CALLBACK(text_inserted),
3474                                         compose);
3475
3476         cur_encoding = conv_get_locale_charset_str_no_utf8();
3477
3478         while (fgets(buf, sizeof(buf), fp) != NULL) {
3479                 gchar *str;
3480
3481                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3482                         str = g_strdup(buf);
3483                 else
3484                         str = conv_codeset_strdup
3485                                 (buf, cur_encoding, CS_INTERNAL);
3486                 if (!str) continue;
3487
3488                 /* strip <CR> if DOS/Windows file,
3489                    replace <CR> with <LF> if Macintosh file. */
3490                 strcrchomp(str);
3491                 len = strlen(str);
3492                 if (len > 0 && str[len - 1] != '\n') {
3493                         while (--len >= 0)
3494                                 if (str[len] == '\r') str[len] = '\n';
3495                 }
3496
3497                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3498                 g_free(str);
3499         }
3500
3501         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3502                                           G_CALLBACK(text_inserted),
3503                                           compose);
3504         compose->autowrap = prev_autowrap;
3505         if (compose->autowrap)
3506                 compose_wrap_all(compose);
3507
3508         fclose(fp);
3509
3510         if (badtxt)
3511                 return COMPOSE_INSERT_INVALID_CHARACTER;
3512         else 
3513                 return COMPOSE_INSERT_SUCCESS;
3514 }
3515
3516 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3517                                   const gchar *filename,
3518                                   const gchar *content_type)
3519 {
3520         AttachInfo *ainfo;
3521         GtkTreeIter iter;
3522         FILE *fp;
3523         off_t size;
3524         GAuto *auto_ainfo;
3525         gchar *size_text;
3526         GtkListStore *store;
3527         gchar *name;
3528         gboolean has_binary = FALSE;
3529
3530         if (!is_file_exist(file)) {
3531                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3532                 gboolean result = FALSE;
3533                 if (file_from_uri && is_file_exist(file_from_uri)) {
3534                         result = compose_attach_append(
3535                                                 compose, file_from_uri,
3536                                                 filename,
3537                                                 content_type);
3538                 }
3539                 g_free(file_from_uri);
3540                 if (result)
3541                         return TRUE;
3542                 alertpanel_error("File %s doesn't exist\n", filename);
3543                 return FALSE;
3544         }
3545         if ((size = get_file_size(file)) < 0) {
3546                 alertpanel_error("Can't get file size of %s\n", filename);
3547                 return FALSE;
3548         }
3549         if (size == 0) {
3550                 alertpanel_error(_("File %s is empty."), filename);
3551                 return FALSE;
3552         }
3553         if ((fp = g_fopen(file, "rb")) == NULL) {
3554                 alertpanel_error(_("Can't read %s."), filename);
3555                 return FALSE;
3556         }
3557         fclose(fp);
3558
3559         ainfo = g_new0(AttachInfo, 1);
3560         auto_ainfo = g_auto_pointer_new_with_free
3561                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3562         ainfo->file = g_strdup(file);
3563
3564         if (content_type) {
3565                 ainfo->content_type = g_strdup(content_type);
3566                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3567                         MsgInfo *msginfo;
3568                         MsgFlags flags = {0, 0};
3569
3570                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3571                                 ainfo->encoding = ENC_7BIT;
3572                         else
3573                                 ainfo->encoding = ENC_8BIT;
3574
3575                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3576                         if (msginfo && msginfo->subject)
3577                                 name = g_strdup(msginfo->subject);
3578                         else
3579                                 name = g_path_get_basename(filename ? filename : file);
3580
3581                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3582
3583                         procmsg_msginfo_free(msginfo);
3584                 } else {
3585                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3586                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3587                         else
3588                                 ainfo->encoding = ENC_BASE64;
3589                         name = g_path_get_basename(filename ? filename : file);
3590                         ainfo->name = g_strdup(name);
3591                 }
3592                 g_free(name);
3593         } else {
3594                 ainfo->content_type = procmime_get_mime_type(file);
3595                 if (!ainfo->content_type) {
3596                         ainfo->content_type =
3597                                 g_strdup("application/octet-stream");
3598                         ainfo->encoding = ENC_BASE64;
3599                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3600                         ainfo->encoding =
3601                                 procmime_get_encoding_for_text_file(file, &has_binary);
3602                 else
3603                         ainfo->encoding = ENC_BASE64;
3604                 name = g_path_get_basename(filename ? filename : file);
3605                 ainfo->name = g_strdup(name);   
3606                 g_free(name);
3607         }
3608
3609         if (ainfo->name != NULL
3610         &&  !strcmp(ainfo->name, ".")) {
3611                 g_free(ainfo->name);
3612                 ainfo->name = NULL;
3613         }
3614
3615         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3616                 g_free(ainfo->content_type);
3617                 ainfo->content_type = g_strdup("application/octet-stream");
3618         }
3619
3620         ainfo->size = (goffset)size;
3621         size_text = to_human_readable((goffset)size);
3622
3623         store = GTK_LIST_STORE(gtk_tree_view_get_model
3624                         (GTK_TREE_VIEW(compose->attach_clist)));
3625                 
3626         gtk_list_store_append(store, &iter);
3627         gtk_list_store_set(store, &iter, 
3628                            COL_MIMETYPE, ainfo->content_type,
3629                            COL_SIZE, size_text,
3630                            COL_NAME, ainfo->name,
3631                            COL_DATA, ainfo,
3632                            COL_AUTODATA, auto_ainfo,
3633                            -1);
3634         
3635         g_auto_pointer_free(auto_ainfo);
3636         compose_attach_update_label(compose);
3637         return TRUE;
3638 }
3639
3640 static void compose_use_signing(Compose *compose, gboolean use_signing)
3641 {
3642         compose->use_signing = use_signing;
3643         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3644 }
3645
3646 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3647 {
3648         compose->use_encryption = use_encryption;
3649         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3650 }
3651
3652 #define NEXT_PART_NOT_CHILD(info)  \
3653 {  \
3654         node = info->node;  \
3655         while (node->children)  \
3656                 node = g_node_last_child(node);  \
3657         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3658 }
3659
3660 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3661 {
3662         MimeInfo *mimeinfo;
3663         MimeInfo *child;
3664         MimeInfo *firsttext = NULL;
3665         MimeInfo *encrypted = NULL;
3666         GNode    *node;
3667         gchar *outfile;
3668         const gchar *partname = NULL;
3669
3670         mimeinfo = procmime_scan_message(msginfo);
3671         if (!mimeinfo) return;
3672
3673         if (mimeinfo->node->children == NULL) {
3674                 procmime_mimeinfo_free_all(mimeinfo);
3675                 return;
3676         }
3677
3678         /* find first content part */
3679         child = (MimeInfo *) mimeinfo->node->children->data;
3680         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3681                 child = (MimeInfo *)child->node->children->data;
3682
3683         if (child) {
3684                 if (child->type == MIMETYPE_TEXT) {
3685                         firsttext = child;
3686                         debug_print("First text part found\n");
3687                 } else if (compose->mode == COMPOSE_REEDIT &&
3688                          child->type == MIMETYPE_APPLICATION &&
3689                          !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3690                         encrypted = (MimeInfo *)child->node->parent->data;
3691                 }
3692         }
3693         child = (MimeInfo *) mimeinfo->node->children->data;
3694         while (child != NULL) {
3695                 gint err;
3696
3697                 if (child == encrypted) {
3698                         /* skip this part of tree */
3699                         NEXT_PART_NOT_CHILD(child);
3700                         continue;
3701                 }
3702
3703                 if (child->type == MIMETYPE_MULTIPART) {
3704                         /* get the actual content */
3705                         child = procmime_mimeinfo_next(child);
3706                         continue;
3707                 }
3708                     
3709                 if (child == firsttext) {
3710                         child = procmime_mimeinfo_next(child);
3711                         continue;
3712                 }
3713
3714                 outfile = procmime_get_tmp_file_name(child);
3715                 if ((err = procmime_get_part(outfile, child)) < 0)
3716                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3717                 else {
3718                         gchar *content_type;
3719
3720                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3721
3722                         /* if we meet a pgp signature, we don't attach it, but
3723                          * we force signing. */
3724                         if ((strcmp(content_type, "application/pgp-signature") &&
3725                             strcmp(content_type, "application/pkcs7-signature") &&
3726                             strcmp(content_type, "application/x-pkcs7-signature"))
3727                             || compose->mode == COMPOSE_REDIRECT) {
3728                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3729                                 if (partname == NULL)
3730                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3731                                 if (partname == NULL)
3732                                         partname = "";
3733                                 compose_attach_append(compose, outfile, 
3734                                                       partname, content_type);
3735                         } else {
3736                                 compose_force_signing(compose, compose->account, NULL);
3737                         }
3738                         g_free(content_type);
3739                 }
3740                 g_free(outfile);
3741                 NEXT_PART_NOT_CHILD(child);
3742         }
3743         procmime_mimeinfo_free_all(mimeinfo);
3744 }
3745
3746 #undef NEXT_PART_NOT_CHILD
3747
3748
3749
3750 typedef enum {
3751         WAIT_FOR_INDENT_CHAR,
3752         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3753 } IndentState;
3754
3755 /* return indent length, we allow:
3756    indent characters followed by indent characters or spaces/tabs,
3757    alphabets and numbers immediately followed by indent characters,
3758    and the repeating sequences of the above
3759    If quote ends with multiple spaces, only the first one is included. */
3760 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3761                                     const GtkTextIter *start, gint *len)
3762 {
3763         GtkTextIter iter = *start;
3764         gunichar wc;
3765         gchar ch[6];
3766         gint clen;
3767         IndentState state = WAIT_FOR_INDENT_CHAR;
3768         gboolean is_space;
3769         gboolean is_indent;
3770         gint alnum_count = 0;
3771         gint space_count = 0;
3772         gint quote_len = 0;
3773
3774         if (prefs_common.quote_chars == NULL) {
3775                 return 0 ;
3776         }
3777
3778         while (!gtk_text_iter_ends_line(&iter)) {
3779                 wc = gtk_text_iter_get_char(&iter);
3780                 if (g_unichar_iswide(wc))
3781                         break;
3782                 clen = g_unichar_to_utf8(wc, ch);
3783                 if (clen != 1)
3784                         break;
3785
3786                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3787                 is_space = g_unichar_isspace(wc);
3788
3789                 if (state == WAIT_FOR_INDENT_CHAR) {
3790                         if (!is_indent && !g_unichar_isalnum(wc))
3791                                 break;
3792                         if (is_indent) {
3793                                 quote_len += alnum_count + space_count + 1;
3794                                 alnum_count = space_count = 0;
3795                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3796                         } else
3797                                 alnum_count++;
3798                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3799                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3800                                 break;
3801                         if (is_space)
3802                                 space_count++;
3803                         else if (is_indent) {
3804                                 quote_len += alnum_count + space_count + 1;
3805                                 alnum_count = space_count = 0;
3806                         } else {
3807                                 alnum_count++;
3808                                 state = WAIT_FOR_INDENT_CHAR;
3809                         }
3810                 }
3811
3812                 gtk_text_iter_forward_char(&iter);
3813         }
3814
3815         if (quote_len > 0 && space_count > 0)
3816                 quote_len++;
3817
3818         if (len)
3819                 *len = quote_len;
3820
3821         if (quote_len > 0) {
3822                 iter = *start;
3823                 gtk_text_iter_forward_chars(&iter, quote_len);
3824                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3825         }
3826
3827         return NULL;
3828 }
3829
3830 /* return >0 if the line is itemized */
3831 static int compose_itemized_length(GtkTextBuffer *buffer,
3832                                     const GtkTextIter *start)
3833 {
3834         GtkTextIter iter = *start;
3835         gunichar wc;
3836         gchar ch[6];
3837         gint clen;
3838         gint len = 0;
3839         if (gtk_text_iter_ends_line(&iter))
3840                 return 0;
3841
3842         while (1) {
3843                 len++;
3844                 wc = gtk_text_iter_get_char(&iter);
3845                 if (!g_unichar_isspace(wc))
3846                         break;
3847                 gtk_text_iter_forward_char(&iter);
3848                 if (gtk_text_iter_ends_line(&iter))
3849                         return 0;
3850         }
3851
3852         clen = g_unichar_to_utf8(wc, ch);
3853         if (clen != 1)
3854                 return 0;
3855
3856         if (!strchr("*-+", ch[0]))
3857                 return 0;
3858
3859         gtk_text_iter_forward_char(&iter);
3860         if (gtk_text_iter_ends_line(&iter))
3861                 return 0;
3862         wc = gtk_text_iter_get_char(&iter);
3863         if (g_unichar_isspace(wc)) {
3864                 return len+1;
3865         }
3866         return 0;
3867 }
3868
3869 /* return the string at the start of the itemization */
3870 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3871                                     const GtkTextIter *start)
3872 {
3873         GtkTextIter iter = *start;
3874         gunichar wc;
3875         gint len = 0;
3876         GString *item_chars = g_string_new("");
3877         gchar *str = NULL;
3878
3879         if (gtk_text_iter_ends_line(&iter))
3880                 return NULL;
3881
3882         while (1) {
3883                 len++;
3884                 wc = gtk_text_iter_get_char(&iter);
3885                 if (!g_unichar_isspace(wc))
3886                         break;
3887                 gtk_text_iter_forward_char(&iter);
3888                 if (gtk_text_iter_ends_line(&iter))
3889                         break;
3890                 g_string_append_unichar(item_chars, wc);
3891         }
3892
3893         str = item_chars->str;
3894         g_string_free(item_chars, FALSE);
3895         return str;
3896 }
3897
3898 /* return the number of spaces at a line's start */
3899 static int compose_left_offset_length(GtkTextBuffer *buffer,
3900                                     const GtkTextIter *start)
3901 {
3902         GtkTextIter iter = *start;
3903         gunichar wc;
3904         gint len = 0;
3905         if (gtk_text_iter_ends_line(&iter))
3906                 return 0;
3907
3908         while (1) {
3909                 wc = gtk_text_iter_get_char(&iter);
3910                 if (!g_unichar_isspace(wc))
3911                         break;
3912                 len++;
3913                 gtk_text_iter_forward_char(&iter);
3914                 if (gtk_text_iter_ends_line(&iter))
3915                         return 0;
3916         }
3917
3918         gtk_text_iter_forward_char(&iter);
3919         if (gtk_text_iter_ends_line(&iter))
3920                 return 0;
3921         return len;
3922 }
3923
3924 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3925                                            const GtkTextIter *start,
3926                                            GtkTextIter *break_pos,
3927                                            gint max_col,
3928                                            gint quote_len)
3929 {
3930         GtkTextIter iter = *start, line_end = *start;
3931         PangoLogAttr *attrs;
3932         gchar *str;
3933         gchar *p;
3934         gint len;
3935         gint i;
3936         gint col = 0;
3937         gint pos = 0;
3938         gboolean can_break = FALSE;
3939         gboolean do_break = FALSE;
3940         gboolean was_white = FALSE;
3941         gboolean prev_dont_break = FALSE;
3942
3943         gtk_text_iter_forward_to_line_end(&line_end);
3944         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3945         len = g_utf8_strlen(str, -1);
3946         
3947         if (len == 0) {
3948                 g_free(str);
3949                 g_warning("compose_get_line_break_pos: len = 0!\n");
3950                 return FALSE;
3951         }
3952
3953         /* g_print("breaking line: %d: %s (len = %d)\n",
3954                 gtk_text_iter_get_line(&iter), str, len); */
3955
3956         attrs = g_new(PangoLogAttr, len + 1);
3957
3958         pango_default_break(str, -1, NULL, attrs, len + 1);
3959
3960         p = str;
3961
3962         /* skip quote and leading spaces */
3963         for (i = 0; *p != '\0' && i < len; i++) {
3964                 gunichar wc;
3965
3966                 wc = g_utf8_get_char(p);
3967                 if (i >= quote_len && !g_unichar_isspace(wc))
3968                         break;
3969                 if (g_unichar_iswide(wc))
3970                         col += 2;
3971                 else if (*p == '\t')
3972                         col += 8;
3973                 else
3974                         col++;
3975                 p = g_utf8_next_char(p);
3976         }
3977
3978         for (; *p != '\0' && i < len; i++) {
3979                 PangoLogAttr *attr = attrs + i;
3980                 gunichar wc;
3981                 gint uri_len;
3982
3983                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3984                         pos = i;
3985                 
3986                 was_white = attr->is_white;
3987
3988                 /* don't wrap URI */
3989                 if ((uri_len = get_uri_len(p)) > 0) {
3990                         col += uri_len;
3991                         if (pos > 0 && col > max_col) {
3992                                 do_break = TRUE;
3993                                 break;
3994                         }
3995                         i += uri_len - 1;
3996                         p += uri_len;
3997                         can_break = TRUE;
3998                         continue;
3999                 }
4000
4001                 wc = g_utf8_get_char(p);
4002                 if (g_unichar_iswide(wc)) {
4003                         col += 2;
4004                         if (prev_dont_break && can_break && attr->is_line_break)
4005                                 pos = i;
4006                 } else if (*p == '\t')
4007                         col += 8;
4008                 else
4009                         col++;
4010                 if (pos > 0 && col > max_col) {
4011                         do_break = TRUE;
4012                         break;
4013                 }
4014
4015                 if (*p == '-' || *p == '/')
4016                         prev_dont_break = TRUE;
4017                 else
4018                         prev_dont_break = FALSE;
4019
4020                 p = g_utf8_next_char(p);
4021                 can_break = TRUE;
4022         }
4023
4024 //      debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4025
4026         g_free(attrs);
4027         g_free(str);
4028
4029         *break_pos = *start;
4030         gtk_text_iter_set_line_offset(break_pos, pos);
4031
4032         return do_break;
4033 }
4034
4035 static gboolean compose_join_next_line(Compose *compose,
4036                                        GtkTextBuffer *buffer,
4037                                        GtkTextIter *iter,
4038                                        const gchar *quote_str)
4039 {
4040         GtkTextIter iter_ = *iter, cur, prev, next, end;
4041         PangoLogAttr attrs[3];
4042         gchar *str;
4043         gchar *next_quote_str;
4044         gunichar wc1, wc2;
4045         gint quote_len;
4046         gboolean keep_cursor = FALSE;
4047
4048         if (!gtk_text_iter_forward_line(&iter_) ||
4049             gtk_text_iter_ends_line(&iter_)) {
4050                 return FALSE;
4051         }
4052         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
4053
4054         if ((quote_str || next_quote_str) &&
4055             strcmp2(quote_str, next_quote_str) != 0) {
4056                 g_free(next_quote_str);
4057                 return FALSE;
4058         }
4059         g_free(next_quote_str);
4060
4061         end = iter_;
4062         if (quote_len > 0) {
4063                 gtk_text_iter_forward_chars(&end, quote_len);
4064                 if (gtk_text_iter_ends_line(&end)) {
4065                         return FALSE;
4066                 }
4067         }
4068
4069         /* don't join itemized lines */
4070         if (compose_itemized_length(buffer, &end) > 0) {
4071                 return FALSE;
4072         }
4073
4074         /* don't join signature separator */
4075         if (compose_is_sig_separator(compose, buffer, &iter_)) {
4076                 return FALSE;
4077         }
4078         /* delete quote str */
4079         if (quote_len > 0)
4080                 gtk_text_buffer_delete(buffer, &iter_, &end);
4081
4082         /* don't join line breaks put by the user */
4083         prev = cur = iter_;
4084         gtk_text_iter_backward_char(&cur);
4085         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4086                 gtk_text_iter_forward_char(&cur);
4087                 *iter = cur;
4088                 return FALSE;
4089         }
4090         gtk_text_iter_forward_char(&cur);
4091         /* delete linebreak and extra spaces */
4092         while (gtk_text_iter_backward_char(&cur)) {
4093                 wc1 = gtk_text_iter_get_char(&cur);
4094                 if (!g_unichar_isspace(wc1))
4095                         break;
4096                 prev = cur;
4097         }
4098         next = cur = iter_;
4099         while (!gtk_text_iter_ends_line(&cur)) {
4100                 wc1 = gtk_text_iter_get_char(&cur);
4101                 if (!g_unichar_isspace(wc1))
4102                         break;
4103                 gtk_text_iter_forward_char(&cur);
4104                 next = cur;
4105         }
4106         if (!gtk_text_iter_equal(&prev, &next)) {
4107                 GtkTextMark *mark;
4108
4109                 mark = gtk_text_buffer_get_insert(buffer);
4110                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4111                 if (gtk_text_iter_equal(&prev, &cur))
4112                         keep_cursor = TRUE;
4113                 gtk_text_buffer_delete(buffer, &prev, &next);
4114         }
4115         iter_ = prev;
4116
4117         /* insert space if required */
4118         gtk_text_iter_backward_char(&prev);
4119         wc1 = gtk_text_iter_get_char(&prev);
4120         wc2 = gtk_text_iter_get_char(&next);
4121         gtk_text_iter_forward_char(&next);
4122         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4123         pango_default_break(str, -1, NULL, attrs, 3);
4124         if (!attrs[1].is_line_break ||
4125             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4126                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4127                 if (keep_cursor) {
4128                         gtk_text_iter_backward_char(&iter_);
4129                         gtk_text_buffer_place_cursor(buffer, &iter_);
4130                 }
4131         }
4132         g_free(str);
4133
4134         *iter = iter_;
4135         return TRUE;
4136 }
4137
4138 #define ADD_TXT_POS(bp_, ep_, pti_) \
4139         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4140                 last = last->next; \
4141                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4142                 last->next = NULL; \
4143         } else { \
4144                 g_warning("alloc error scanning URIs\n"); \
4145         }
4146
4147 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4148 {
4149         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4150         GtkTextBuffer *buffer;
4151         GtkTextIter iter, break_pos, end_of_line;
4152         gchar *quote_str = NULL;
4153         gint quote_len;
4154         gboolean wrap_quote = prefs_common.linewrap_quote;
4155         gboolean prev_autowrap = compose->autowrap;
4156         gint startq_offset = -1, noq_offset = -1;
4157         gint uri_start = -1, uri_stop = -1;
4158         gint nouri_start = -1, nouri_stop = -1;
4159         gint num_blocks = 0;
4160         gint quotelevel = -1;
4161         gboolean modified = force;
4162         gboolean removed = FALSE;
4163         gboolean modified_before_remove = FALSE;
4164         gint lines = 0;
4165         gboolean start = TRUE;
4166         gint itemized_len = 0, rem_item_len = 0;
4167         gchar *itemized_chars = NULL;
4168         gboolean item_continuation = FALSE;
4169
4170         if (force) {
4171                 modified = TRUE;
4172         }
4173         if (compose->draft_timeout_tag == -2) {
4174                 modified = TRUE;
4175         }
4176
4177         compose->autowrap = FALSE;
4178
4179         buffer = gtk_text_view_get_buffer(text);
4180         undo_wrapping(compose->undostruct, TRUE);
4181         if (par_iter) {
4182                 iter = *par_iter;
4183         } else {
4184                 GtkTextMark *mark;
4185                 mark = gtk_text_buffer_get_insert(buffer);
4186                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4187         }
4188
4189
4190         if (compose->draft_timeout_tag == -2) {
4191                 if (gtk_text_iter_ends_line(&iter)) {
4192                         while (gtk_text_iter_ends_line(&iter) &&
4193                                gtk_text_iter_forward_line(&iter))
4194                                 ;
4195                 } else {
4196                         while (gtk_text_iter_backward_line(&iter)) {
4197                                 if (gtk_text_iter_ends_line(&iter)) {
4198                                         gtk_text_iter_forward_line(&iter);
4199                                         break;
4200                                 }
4201                         }
4202                 }
4203         } else {
4204                 /* move to line start */
4205                 gtk_text_iter_set_line_offset(&iter, 0);
4206         }
4207         
4208         itemized_len = compose_itemized_length(buffer, &iter);
4209         
4210         if (!itemized_len) {
4211                 itemized_len = compose_left_offset_length(buffer, &iter);
4212                 item_continuation = TRUE;
4213         }
4214
4215         if (itemized_len)
4216                 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4217
4218         /* go until paragraph end (empty line) */
4219         while (start || !gtk_text_iter_ends_line(&iter)) {
4220                 gchar *scanpos = NULL;
4221                 /* parse table - in order of priority */
4222                 struct table {
4223                         const gchar *needle; /* token */
4224
4225                         /* token search function */
4226                         gchar    *(*search)     (const gchar *haystack,
4227                                                  const gchar *needle);
4228                         /* part parsing function */
4229                         gboolean  (*parse)      (const gchar *start,
4230                                                  const gchar *scanpos,
4231                                                  const gchar **bp_,
4232                                                  const gchar **ep_,
4233                                                  gboolean hdr);
4234                         /* part to URI function */
4235                         gchar    *(*build_uri)  (const gchar *bp,
4236                                                  const gchar *ep);
4237                 };
4238
4239                 static struct table parser[] = {
4240                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
4241                         {"https://", strcasestr, get_uri_part,   make_uri_string},
4242                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
4243                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
4244                         {"gopher://",strcasestr, get_uri_part,   make_uri_string},
4245                         {"www.",     strcasestr, get_uri_part,   make_http_string},
4246                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
4247                         {"@",        strcasestr, get_email_part, make_email_string}
4248                 };
4249                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4250                 gint last_index = PARSE_ELEMS;
4251                 gint  n;
4252                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4253                 gint walk_pos;
4254                 
4255                 start = FALSE;
4256                 if (!prev_autowrap && num_blocks == 0) {
4257                         num_blocks++;
4258                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
4259                                         G_CALLBACK(text_inserted),
4260                                         compose);
4261                 }
4262                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4263                         goto colorize;
4264
4265                 uri_start = uri_stop = -1;
4266                 quote_len = 0;
4267                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
4268
4269                 if (quote_str) {
4270 //                      debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4271                         if (startq_offset == -1) 
4272                                 startq_offset = gtk_text_iter_get_offset(&iter);
4273                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4274                         if (quotelevel > 2) {
4275                                 /* recycle colors */
4276                                 if (prefs_common.recycle_quote_colors)
4277                                         quotelevel %= 3;
4278                                 else
4279                                         quotelevel = 2;
4280                         }
4281                         if (!wrap_quote) {
4282                                 goto colorize;
4283                         }
4284                 } else {
4285                         if (startq_offset == -1)
4286                                 noq_offset = gtk_text_iter_get_offset(&iter);
4287                         quotelevel = -1;
4288                 }
4289
4290                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4291                         goto colorize;
4292                 }
4293                 if (gtk_text_iter_ends_line(&iter)) {
4294                         goto colorize;
4295                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4296                                                prefs_common.linewrap_len,
4297                                                quote_len)) {
4298                         GtkTextIter prev, next, cur;
4299                         if (prev_autowrap != FALSE || force) {
4300                                 compose->automatic_break = TRUE;
4301                                 modified = TRUE;
4302                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4303                                 compose->automatic_break = FALSE;
4304                                 if (itemized_len && compose->autoindent) {
4305                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4306                                         if (!item_continuation)
4307                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4308                                 }
4309                         } else if (quote_str && wrap_quote) {
4310                                 compose->automatic_break = TRUE;
4311                                 modified = TRUE;
4312                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4313                                 compose->automatic_break = FALSE;
4314                                 if (itemized_len && compose->autoindent) {
4315                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4316                                         if (!item_continuation)
4317                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4318                                 }
4319                         } else 
4320                                 goto colorize;
4321                         /* remove trailing spaces */
4322                         cur = break_pos;
4323                         rem_item_len = itemized_len;
4324                         while (compose->autoindent && rem_item_len-- > 0)
4325                                 gtk_text_iter_backward_char(&cur);
4326                         gtk_text_iter_backward_char(&cur);
4327
4328                         prev = next = cur;
4329                         while (!gtk_text_iter_starts_line(&cur)) {
4330                                 gunichar wc;
4331
4332                                 gtk_text_iter_backward_char(&cur);
4333                                 wc = gtk_text_iter_get_char(&cur);
4334                                 if (!g_unichar_isspace(wc))
4335                                         break;
4336                                 prev = cur;
4337                         }
4338                         if (!gtk_text_iter_equal(&prev, &next)) {
4339                                 gtk_text_buffer_delete(buffer, &prev, &next);
4340                                 break_pos = next;
4341                                 gtk_text_iter_forward_char(&break_pos);
4342                         }
4343
4344                         if (quote_str)
4345                                 gtk_text_buffer_insert(buffer, &break_pos,
4346                                                        quote_str, -1);
4347
4348                         iter = break_pos;
4349                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4350
4351                         /* move iter to current line start */
4352                         gtk_text_iter_set_line_offset(&iter, 0);
4353                         if (quote_str) {
4354                                 g_free(quote_str);
4355                                 quote_str = NULL;
4356                         }
4357                         continue;       
4358                 } else {
4359                         /* move iter to next line start */
4360                         iter = break_pos;
4361                         lines++;
4362                 }
4363
4364 colorize:
4365                 if (!prev_autowrap && num_blocks > 0) {
4366                         num_blocks--;
4367                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4368                                         G_CALLBACK(text_inserted),
4369                                         compose);
4370                 }
4371                 end_of_line = iter;
4372                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4373                         gtk_text_iter_forward_char(&end_of_line);
4374                 }
4375                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4376
4377                 nouri_start = gtk_text_iter_get_offset(&iter);
4378                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4379
4380                 walk_pos = gtk_text_iter_get_offset(&iter);
4381                 /* FIXME: this looks phony. scanning for anything in the parse table */
4382                 for (n = 0; n < PARSE_ELEMS; n++) {
4383                         gchar *tmp;
4384
4385                         tmp = parser[n].search(walk, parser[n].needle);
4386                         if (tmp) {
4387                                 if (scanpos == NULL || tmp < scanpos) {
4388                                         scanpos = tmp;
4389                                         last_index = n;
4390                                 }
4391                         }                                       
4392                 }
4393
4394                 bp = ep = 0;
4395                 if (scanpos) {
4396                         /* check if URI can be parsed */
4397                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4398                                         (const gchar **)&ep, FALSE)
4399                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4400                                         walk = ep;
4401                         } else
4402                                 walk = scanpos +
4403                                         strlen(parser[last_index].needle);
4404                 } 
4405                 if (bp && ep) {
4406                         uri_start = walk_pos + (bp - o_walk);
4407                         uri_stop  = walk_pos + (ep - o_walk);
4408                 }
4409                 g_free(o_walk);
4410                 o_walk = NULL;
4411                 gtk_text_iter_forward_line(&iter);
4412                 g_free(quote_str);
4413                 quote_str = NULL;
4414                 if (startq_offset != -1) {
4415                         GtkTextIter startquote, endquote;
4416                         gtk_text_buffer_get_iter_at_offset(
4417                                 buffer, &startquote, startq_offset);
4418                         endquote = iter;
4419
4420                         switch (quotelevel) {
4421                         case 0: 
4422                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4423                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4424                                         gtk_text_buffer_apply_tag_by_name(
4425                                                 buffer, "quote0", &startquote, &endquote);
4426                                         gtk_text_buffer_remove_tag_by_name(
4427                                                 buffer, "quote1", &startquote, &endquote);
4428                                         gtk_text_buffer_remove_tag_by_name(
4429                                                 buffer, "quote2", &startquote, &endquote);
4430                                         modified = TRUE;
4431                                 }
4432                                 break;
4433                         case 1: 
4434                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4435                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4436                                         gtk_text_buffer_apply_tag_by_name(
4437                                                 buffer, "quote1", &startquote, &endquote);
4438                                         gtk_text_buffer_remove_tag_by_name(
4439                                                 buffer, "quote0", &startquote, &endquote);
4440                                         gtk_text_buffer_remove_tag_by_name(
4441                                                 buffer, "quote2", &startquote, &endquote);
4442                                         modified = TRUE;
4443                                 }
4444                                 break;
4445                         case 2: 
4446                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4447                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4448                                         gtk_text_buffer_apply_tag_by_name(
4449                                                 buffer, "quote2", &startquote, &endquote);
4450                                         gtk_text_buffer_remove_tag_by_name(
4451                                                 buffer, "quote0", &startquote, &endquote);
4452                                         gtk_text_buffer_remove_tag_by_name(
4453                                                 buffer, "quote1", &startquote, &endquote);
4454                                         modified = TRUE;
4455                                 }
4456                                 break;
4457                         }
4458                         startq_offset = -1;
4459                 } else if (noq_offset != -1) {
4460                         GtkTextIter startnoquote, endnoquote;
4461                         gtk_text_buffer_get_iter_at_offset(
4462                                 buffer, &startnoquote, noq_offset);
4463                         endnoquote = iter;
4464
4465                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4466                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4467                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4468                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4469                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4470                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4471                                 gtk_text_buffer_remove_tag_by_name(
4472                                         buffer, "quote0", &startnoquote, &endnoquote);
4473                                 gtk_text_buffer_remove_tag_by_name(
4474                                         buffer, "quote1", &startnoquote, &endnoquote);
4475                                 gtk_text_buffer_remove_tag_by_name(
4476                                         buffer, "quote2", &startnoquote, &endnoquote);
4477                                 modified = TRUE;
4478                         }
4479                         noq_offset = -1;
4480                 }
4481                 
4482                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4483                         GtkTextIter nouri_start_iter, nouri_end_iter;
4484                         gtk_text_buffer_get_iter_at_offset(
4485                                 buffer, &nouri_start_iter, nouri_start);
4486                         gtk_text_buffer_get_iter_at_offset(
4487                                 buffer, &nouri_end_iter, nouri_stop);
4488                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4489                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4490                                 gtk_text_buffer_remove_tag_by_name(
4491                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4492                                 modified_before_remove = modified;
4493                                 modified = TRUE;
4494                                 removed = TRUE;
4495                         }
4496                 }
4497                 if (uri_start >= 0 && uri_stop > 0) {
4498                         GtkTextIter uri_start_iter, uri_end_iter, back;
4499                         gtk_text_buffer_get_iter_at_offset(
4500                                 buffer, &uri_start_iter, uri_start);
4501                         gtk_text_buffer_get_iter_at_offset(
4502                                 buffer, &uri_end_iter, uri_stop);
4503                         back = uri_end_iter;
4504                         gtk_text_iter_backward_char(&back);
4505                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4506                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4507                                 gtk_text_buffer_apply_tag_by_name(
4508                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4509                                 modified = TRUE;
4510                                 if (removed && !modified_before_remove) {
4511                                         modified = FALSE;
4512                                 } 
4513                         }
4514                 }
4515                 if (!modified) {
4516 //                      debug_print("not modified, out after %d lines\n", lines);
4517                         goto end;
4518                 }
4519         }
4520 //      debug_print("modified, out after %d lines\n", lines);
4521 end:
4522         g_free(itemized_chars);
4523         if (par_iter)
4524                 *par_iter = iter;
4525         undo_wrapping(compose->undostruct, FALSE);
4526         compose->autowrap = prev_autowrap;
4527         
4528         return modified;
4529 }
4530
4531 void compose_action_cb(void *data)
4532 {
4533         Compose *compose = (Compose *)data;
4534         compose_wrap_all(compose);
4535 }
4536
4537 static void compose_wrap_all(Compose *compose)
4538 {
4539         compose_wrap_all_full(compose, FALSE);
4540 }
4541
4542 static void compose_wrap_all_full(Compose *compose, gboolean force)
4543 {
4544         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4545         GtkTextBuffer *buffer;
4546         GtkTextIter iter;
4547         gboolean modified = TRUE;
4548
4549         buffer = gtk_text_view_get_buffer(text);
4550
4551         gtk_text_buffer_get_start_iter(buffer, &iter);
4552         while (!gtk_text_iter_is_end(&iter) && modified)
4553                 modified = compose_beautify_paragraph(compose, &iter, force);
4554
4555 }
4556
4557 static void compose_set_title(Compose *compose)
4558 {
4559         gchar *str;
4560         gchar *edited;
4561         gchar *subject;
4562         
4563         edited = compose->modified ? _(" [Edited]") : "";
4564         
4565         subject = gtk_editable_get_chars(
4566                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4567
4568 #ifndef GENERIC_UMPC
4569         if (subject && strlen(subject))
4570                 str = g_strdup_printf(_("%s - Compose message%s"),
4571                                       subject, edited); 
4572         else
4573                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4574 #else
4575         str = g_strdup(_("Compose message"));
4576 #endif
4577
4578         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4579         g_free(str);
4580         g_free(subject);
4581 }
4582
4583 /**
4584  * compose_current_mail_account:
4585  * 
4586  * Find a current mail account (the currently selected account, or the
4587  * default account, if a news account is currently selected).  If a
4588  * mail account cannot be found, display an error message.
4589  * 
4590  * Return value: Mail account, or NULL if not found.
4591  **/
4592 static PrefsAccount *
4593 compose_current_mail_account(void)
4594 {
4595         PrefsAccount *ac;
4596
4597         if (cur_account && cur_account->protocol != A_NNTP)
4598                 ac = cur_account;
4599         else {
4600                 ac = account_get_default();
4601                 if (!ac || ac->protocol == A_NNTP) {
4602                         alertpanel_error(_("Account for sending mail is not specified.\n"
4603                                            "Please select a mail account before sending."));
4604                         return NULL;
4605                 }
4606         }
4607         return ac;
4608 }
4609
4610 #define QUOTE_IF_REQUIRED(out, str)                                     \
4611 {                                                                       \
4612         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4613                 gchar *__tmp;                                           \
4614                 gint len;                                               \
4615                                                                         \
4616                 len = strlen(str) + 3;                                  \
4617                 if ((__tmp = alloca(len)) == NULL) {                    \
4618                         g_warning("can't allocate memory\n");           \
4619                         g_string_free(header, TRUE);                    \
4620                         return NULL;                                    \
4621                 }                                                       \
4622                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4623                 out = __tmp;                                            \
4624         } else {                                                        \
4625                 gchar *__tmp;                                           \
4626                                                                         \
4627                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4628                         g_warning("can't allocate memory\n");           \
4629                         g_string_free(header, TRUE);                    \
4630                         return NULL;                                    \
4631                 } else                                                  \
4632                         strcpy(__tmp, str);                             \
4633                                                                         \
4634                 out = __tmp;                                            \
4635         }                                                               \
4636 }
4637
4638 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4639 {                                                                       \
4640         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4641                 gchar *__tmp;                                           \
4642                 gint len;                                               \
4643                                                                         \
4644                 len = strlen(str) + 3;                                  \
4645                 if ((__tmp = alloca(len)) == NULL) {                    \
4646                         g_warning("can't allocate memory\n");           \
4647                         errret;                                         \
4648                 }                                                       \
4649                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4650                 out = __tmp;                                            \
4651         } else {                                                        \
4652                 gchar *__tmp;                                           \
4653                                                                         \
4654                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4655                         g_warning("can't allocate memory\n");           \
4656                         errret;                                         \
4657                 } else                                                  \
4658                         strcpy(__tmp, str);                             \
4659                                                                         \
4660                 out = __tmp;                                            \
4661         }                                                               \
4662 }
4663
4664 static void compose_select_account(Compose *compose, PrefsAccount *account,
4665                                    gboolean init)
4666 {
4667         gchar *from = NULL, *header;
4668         ComposeHeaderEntry *header_entry;
4669
4670         cm_return_if_fail(account != NULL);
4671
4672         compose->account = account;
4673         if (account->name && *account->name) {
4674                 gchar *buf;
4675                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4676                 from = g_strdup_printf("%s <%s>",
4677                                        buf, account->address);
4678                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4679         } else {
4680                 from = g_strdup_printf("<%s>",
4681                                        account->address);
4682                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4683         }
4684
4685         g_free(from);
4686
4687         compose_set_title(compose);
4688
4689         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4690                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4691         else
4692                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4693         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4694                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4695         else
4696                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4697                                        
4698         activate_privacy_system(compose, account, FALSE);
4699
4700         if (!init && compose->mode != COMPOSE_REDIRECT) {
4701                 undo_block(compose->undostruct);
4702                 compose_insert_sig(compose, TRUE);
4703                 undo_unblock(compose->undostruct);
4704         }
4705         
4706         header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4707         header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4708         
4709         if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4710                 if (account->protocol == A_NNTP) {
4711                         if (!strcmp(header, _("To:")))
4712                                 combobox_select_by_text(
4713                                         GTK_COMBO_BOX(header_entry->combo),
4714                                         _("Newsgroups:"));
4715                 } else {
4716                         if (!strcmp(header, _("Newsgroups:")))
4717                                 combobox_select_by_text(
4718                                         GTK_COMBO_BOX(header_entry->combo),
4719                                         _("To:"));
4720                 }
4721                 
4722         }
4723         g_free(header);
4724         
4725 #ifdef USE_ENCHANT
4726         /* use account's dict info if set */
4727         if (compose->gtkaspell) {
4728                 if (account->enable_default_dictionary)
4729                         gtkaspell_change_dict(compose->gtkaspell,
4730                                         account->default_dictionary, FALSE);
4731                 if (account->enable_default_alt_dictionary)
4732                         gtkaspell_change_alt_dict(compose->gtkaspell,
4733                                         account->default_alt_dictionary);
4734                 if (account->enable_default_dictionary
4735                         || account->enable_default_alt_dictionary)
4736                         compose_spell_menu_changed(compose);
4737         }
4738 #endif
4739 }
4740
4741 gboolean compose_check_for_valid_recipient(Compose *compose) {
4742         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4743         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4744         gboolean recipient_found = FALSE;
4745         GSList *list;
4746         gchar **strptr;
4747
4748         /* free to and newsgroup list */
4749         slist_free_strings(compose->to_list);
4750         g_slist_free(compose->to_list);
4751         compose->to_list = NULL;
4752                         
4753         slist_free_strings(compose->newsgroup_list);
4754         g_slist_free(compose->newsgroup_list);
4755         compose->newsgroup_list = NULL;
4756
4757         /* search header entries for to and newsgroup entries */
4758         for (list = compose->header_list; list; list = list->next) {
4759                 gchar *header;
4760                 gchar *entry;
4761                 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4762                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4763                 g_strstrip(entry);
4764                 g_strstrip(header);
4765                 if (entry[0] != '\0') {
4766                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4767                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4768                                         compose->to_list = address_list_append(compose->to_list, entry);
4769                                         recipient_found = TRUE;
4770                                 }
4771                         }
4772                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4773                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4774                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4775                                         recipient_found = TRUE;
4776                                 }
4777                         }
4778                 }
4779                 g_free(header);
4780                 g_free(entry);
4781         }
4782         return recipient_found;
4783 }
4784
4785 static gboolean compose_check_for_set_recipients(Compose *compose)
4786 {
4787         if (compose->account->set_autocc && compose->account->auto_cc) {
4788                 gboolean found_other = FALSE;
4789                 GSList *list;
4790                 /* search header entries for to and newsgroup entries */
4791                 for (list = compose->header_list; list; list = list->next) {
4792                         gchar *entry;
4793                         gchar *header;
4794                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4795                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4796                         g_strstrip(entry);
4797                         g_strstrip(header);
4798                         if (strcmp(entry, compose->account->auto_cc)
4799                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4800                                 found_other = TRUE;
4801                                 g_free(entry);
4802                                 break;
4803                         }
4804                         g_free(entry);
4805                         g_free(header);
4806                 }
4807                 if (!found_other) {
4808                         AlertValue aval;
4809                         if (compose->batch) {
4810                                 gtk_widget_show_all(compose->window);
4811                         }
4812                         aval = alertpanel(_("Send"),
4813                                           _("The only recipient is the default CC address. Send anyway?"),
4814                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4815                         if (aval != G_ALERTALTERNATE)
4816                                 return FALSE;
4817                 }
4818         }
4819         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4820                 gboolean found_other = FALSE;
4821                 GSList *list;
4822                 /* search header entries for to and newsgroup entries */
4823                 for (list = compose->header_list; list; list = list->next) {
4824                         gchar *entry;
4825                         gchar *header;
4826                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4827                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4828                         g_strstrip(entry);
4829                         g_strstrip(header);
4830                         if (strcmp(entry, compose->account->auto_bcc)
4831                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4832                                 found_other = TRUE;
4833                                 g_free(entry);
4834                                 break;
4835                         }
4836                         g_free(entry);
4837                         g_free(header);
4838                 }
4839                 if (!found_other) {
4840                         AlertValue aval;
4841                         if (compose->batch) {
4842                                 gtk_widget_show_all(compose->window);
4843                         }
4844                         aval = alertpanel(_("Send"),
4845                                           _("The only recipient is the default BCC address. Send anyway?"),
4846                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4847                         if (aval != G_ALERTALTERNATE)
4848                                 return FALSE;
4849                 }
4850         }
4851         return TRUE;
4852 }
4853
4854 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4855 {
4856         const gchar *str;
4857
4858         if (compose_check_for_valid_recipient(compose) == FALSE) {
4859                 if (compose->batch) {
4860                         gtk_widget_show_all(compose->window);
4861                 }
4862                 alertpanel_error(_("Recipient is not specified."));
4863                 return FALSE;
4864         }
4865
4866         if (compose_check_for_set_recipients(compose) == FALSE) {
4867                 return FALSE;
4868         }
4869
4870         if (!compose->batch) {
4871                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4872                 if (*str == '\0' && check_everything == TRUE && 
4873                     compose->mode != COMPOSE_REDIRECT) {
4874                         AlertValue aval;
4875                         gchar *button_label;
4876                         gchar *message;
4877
4878                         if (compose->sending)
4879                                 button_label = _("+_Send");
4880                         else
4881                                 button_label = _("+_Queue");
4882                         message = g_strdup_printf(_("Subject is empty. %s"),
4883                                         compose->sending?_("Send it anyway?"):
4884                                         _("Queue it anyway?"));
4885
4886                         aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4887                                           GTK_STOCK_CANCEL, button_label, NULL);
4888                         g_free(message);
4889                         if (aval != G_ALERTALTERNATE)
4890                                 return FALSE;
4891                 }
4892         }
4893
4894         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4895                 return FALSE;
4896
4897         return TRUE;
4898 }
4899
4900 gint compose_send(Compose *compose)
4901 {
4902         gint msgnum;
4903         FolderItem *folder = NULL;
4904         gint val = -1;
4905         gchar *msgpath = NULL;
4906         gboolean discard_window = FALSE;
4907         gchar *errstr = NULL;
4908         gchar *tmsgid = NULL;
4909         MainWindow *mainwin = mainwindow_get_mainwindow();
4910         gboolean queued_removed = FALSE;
4911
4912         if (prefs_common.send_dialog_invisible
4913                         || compose->batch == TRUE)
4914                 discard_window = TRUE;
4915
4916         compose_allow_user_actions (compose, FALSE);
4917         compose->sending = TRUE;
4918
4919         if (compose_check_entries(compose, TRUE) == FALSE) {
4920                 if (compose->batch) {
4921                         gtk_widget_show_all(compose->window);
4922                 }
4923                 goto bail;
4924         }
4925
4926         inc_lock();
4927         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4928
4929         if (val) {
4930                 if (compose->batch) {
4931                         gtk_widget_show_all(compose->window);
4932                 }
4933                 if (val == -4) {
4934                         alertpanel_error(_("Could not queue message for sending:\n\n"
4935                                            "Charset conversion failed."));
4936                 } else if (val == -5) {
4937                         alertpanel_error(_("Could not queue message for sending:\n\n"
4938                                            "Couldn't get recipient encryption key."));
4939                 } else if (val == -6) {
4940                         /* silent error */
4941                 } else if (val == -3) {
4942                         if (privacy_peek_error())
4943                         alertpanel_error(_("Could not queue message for sending:\n\n"
4944                                            "Signature failed: %s"), privacy_get_error());
4945                 } else if (val == -2 && errno != 0) {
4946                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4947                 } else {
4948                         alertpanel_error(_("Could not queue message for sending."));
4949                 }
4950                 goto bail;
4951         }
4952
4953         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4954         if (discard_window) {
4955                 compose->sending = FALSE;
4956                 compose_close(compose);
4957                 /* No more compose access in the normal codepath 
4958                  * after this point! */
4959                 compose = NULL;
4960         }
4961
4962         if (msgnum == 0) {
4963                 alertpanel_error(_("The message was queued but could not be "
4964                                    "sent.\nUse \"Send queued messages\" from "
4965                                    "the main window to retry."));
4966                 if (!discard_window) {
4967                         goto bail;
4968                 }
4969                 inc_unlock();
4970                 g_free(tmsgid);
4971                 return -1;
4972         }
4973         if (msgpath == NULL) {
4974                 msgpath = folder_item_fetch_msg(folder, msgnum);
4975                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4976                 g_free(msgpath);
4977         } else {
4978                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4979                 claws_unlink(msgpath);
4980                 g_free(msgpath);
4981         }
4982         if (!discard_window) {
4983                 if (val != 0) {
4984                         if (!queued_removed)
4985                                 folder_item_remove_msg(folder, msgnum);
4986                         folder_item_scan(folder);
4987                         if (tmsgid) {
4988                                 /* make sure we delete that */
4989                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4990                                 if (tmp) {
4991                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4992                                         folder_item_remove_msg(folder, tmp->msgnum);
4993                                         procmsg_msginfo_free(tmp);
4994                                 } 
4995                         }
4996                 }
4997         }
4998
4999         if (val == 0) {
5000                 if (!queued_removed)
5001                         folder_item_remove_msg(folder, msgnum);
5002                 folder_item_scan(folder);
5003                 if (tmsgid) {
5004                         /* make sure we delete that */
5005                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5006                         if (tmp) {
5007                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5008                                 folder_item_remove_msg(folder, tmp->msgnum);
5009                                 procmsg_msginfo_free(tmp);
5010                         }
5011                 }
5012                 if (!discard_window) {
5013                         compose->sending = FALSE;
5014                         compose_allow_user_actions (compose, TRUE);
5015                         compose_close(compose);
5016                 }
5017         } else {
5018                 if (errstr) {
5019                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5020                                    "the main window to retry."), errstr);
5021                         g_free(errstr);
5022                 } else {
5023                         alertpanel_error_log(_("The message was queued but could not be "
5024                                    "sent.\nUse \"Send queued messages\" from "
5025                                    "the main window to retry."));
5026                 }
5027                 if (!discard_window) {
5028                         goto bail;              
5029                 }
5030                 inc_unlock();
5031                 g_free(tmsgid);
5032                 return -1;
5033         }
5034         g_free(tmsgid);
5035         inc_unlock();
5036         toolbar_main_set_sensitive(mainwin);
5037         main_window_set_menu_sensitive(mainwin);
5038         return 0;
5039
5040 bail:
5041         inc_unlock();
5042         g_free(tmsgid);
5043         compose_allow_user_actions (compose, TRUE);
5044         compose->sending = FALSE;
5045         compose->modified = TRUE; 
5046         toolbar_main_set_sensitive(mainwin);
5047         main_window_set_menu_sensitive(mainwin);
5048
5049         return -1;
5050 }
5051
5052 static gboolean compose_use_attach(Compose *compose) 
5053 {
5054         GtkTreeModel *model = gtk_tree_view_get_model
5055                                 (GTK_TREE_VIEW(compose->attach_clist));
5056         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5057 }
5058
5059 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5060                                                            FILE *fp)
5061 {
5062         gchar buf[BUFFSIZE];
5063         gchar *str;
5064         gboolean first_to_address;
5065         gboolean first_cc_address;
5066         GSList *list;
5067         ComposeHeaderEntry *headerentry;
5068         const gchar *headerentryname;
5069         const gchar *cc_hdr;
5070         const gchar *to_hdr;
5071         gboolean err = FALSE;
5072
5073         debug_print("Writing redirect header\n");
5074
5075         cc_hdr = prefs_common_translated_header_name("Cc:");
5076         to_hdr = prefs_common_translated_header_name("To:");
5077
5078         first_to_address = TRUE;
5079         for (list = compose->header_list; list; list = list->next) {
5080                 headerentry = ((ComposeHeaderEntry *)list->data);
5081                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5082
5083                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5084                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5085                         Xstrdup_a(str, entstr, return -1);
5086                         g_strstrip(str);
5087                         if (str[0] != '\0') {
5088                                 compose_convert_header
5089                                         (compose, buf, sizeof(buf), str,
5090                                         strlen("Resent-To") + 2, TRUE);
5091
5092                                 if (first_to_address) {
5093                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5094                                         first_to_address = FALSE;
5095                                 } else {
5096                                         err |= (fprintf(fp, ",") < 0);
5097                                 }
5098                                 err |= (fprintf(fp, "%s", buf) < 0);
5099                         }
5100                 }
5101         }
5102         if (!first_to_address) {
5103                 err |= (fprintf(fp, "\n") < 0);
5104         }
5105
5106         first_cc_address = TRUE;
5107         for (list = compose->header_list; list; list = list->next) {
5108                 headerentry = ((ComposeHeaderEntry *)list->data);
5109                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5110
5111                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5112                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5113                         Xstrdup_a(str, strg, return -1);
5114                         g_strstrip(str);
5115                         if (str[0] != '\0') {
5116                                 compose_convert_header
5117                                         (compose, buf, sizeof(buf), str,
5118                                         strlen("Resent-Cc") + 2, TRUE);
5119
5120                                 if (first_cc_address) {
5121                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5122                                         first_cc_address = FALSE;
5123                                 } else {
5124                                         err |= (fprintf(fp, ",") < 0);
5125                                 }
5126                                 err |= (fprintf(fp, "%s", buf) < 0);
5127                         }
5128                 }
5129         }
5130         if (!first_cc_address) {
5131                 err |= (fprintf(fp, "\n") < 0);
5132         }
5133         
5134         return (err ? -1:0);
5135 }
5136
5137 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5138 {
5139         gchar buf[BUFFSIZE];
5140         gchar *str;
5141         const gchar *entstr;
5142         /* struct utsname utsbuf; */
5143         gboolean err = FALSE;
5144
5145         cm_return_val_if_fail(fp != NULL, -1);
5146         cm_return_val_if_fail(compose->account != NULL, -1);
5147         cm_return_val_if_fail(compose->account->address != NULL, -1);
5148
5149         /* Resent-Date */
5150         get_rfc822_date(buf, sizeof(buf));
5151         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5152
5153         /* Resent-From */
5154         if (compose->account->name && *compose->account->name) {
5155                 compose_convert_header
5156                         (compose, buf, sizeof(buf), compose->account->name,
5157                          strlen("From: "), TRUE);
5158                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5159                         buf, compose->account->address) < 0);
5160         } else
5161                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5162
5163         /* Subject */
5164         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5165         if (*entstr != '\0') {
5166                 Xstrdup_a(str, entstr, return -1);
5167                 g_strstrip(str);
5168                 if (*str != '\0') {
5169                         compose_convert_header(compose, buf, sizeof(buf), str,
5170                                                strlen("Subject: "), FALSE);
5171                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5172                 }
5173         }
5174
5175         /* Resent-Message-ID */
5176         if (compose->account->set_domain && compose->account->domain) {
5177                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5178         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5179                 g_snprintf(buf, sizeof(buf), "%s", 
5180                         strchr(compose->account->address, '@') ?
5181                                 strchr(compose->account->address, '@')+1 :
5182                                 compose->account->address);
5183         } else {
5184                 g_snprintf(buf, sizeof(buf), "%s", "");
5185         }
5186
5187         if (compose->account->gen_msgid) {
5188                 gchar *addr = NULL;
5189                 if (compose->account->msgid_with_addr) {
5190                         addr = compose->account->address;
5191                 }
5192                 generate_msgid(buf, sizeof(buf), addr);
5193                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5194                 compose->msgid = g_strdup(buf);
5195         } else {
5196                 compose->msgid = NULL;
5197         }
5198
5199         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5200                 return -1;
5201
5202         /* separator between header and body */
5203         err |= (fputs("\n", fp) == EOF);
5204
5205         return (err ? -1:0);
5206 }
5207
5208 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5209 {
5210         FILE *fp;
5211         size_t len;
5212         gchar buf[BUFFSIZE];
5213         int i = 0;
5214         gboolean skip = FALSE;
5215         gboolean err = FALSE;
5216         gchar *not_included[]={
5217                 "Return-Path:",         "Delivered-To:",        "Received:",
5218                 "Subject:",             "X-UIDL:",              "AF:",
5219                 "NF:",                  "PS:",                  "SRH:",
5220                 "SFN:",                 "DSR:",                 "MID:",
5221                 "CFG:",                 "PT:",                  "S:",
5222                 "RQ:",                  "SSV:",                 "NSV:",
5223                 "SSH:",                 "R:",                   "MAID:",
5224                 "NAID:",                "RMID:",                "FMID:",
5225                 "SCF:",                 "RRCPT:",               "NG:",
5226                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5227                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5228                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5229                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5230                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5231                 NULL
5232                 };
5233         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5234                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5235                 return -1;
5236         }
5237
5238         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5239                 skip = FALSE;
5240                 for (i = 0; not_included[i] != NULL; i++) {
5241                         if (g_ascii_strncasecmp(buf, not_included[i],
5242                                                 strlen(not_included[i])) == 0) {
5243                                 skip = TRUE;
5244                                 break;
5245                         }
5246                 }
5247                 if (skip)
5248                         continue;
5249                 if (fputs(buf, fdest) == -1)
5250                         goto error;
5251
5252                 if (!prefs_common.redirect_keep_from) {
5253                         if (g_ascii_strncasecmp(buf, "From:",
5254                                           strlen("From:")) == 0) {
5255                                 err |= (fputs(" (by way of ", fdest) == EOF);
5256                                 if (compose->account->name
5257                                     && *compose->account->name) {
5258                                         compose_convert_header
5259                                                 (compose, buf, sizeof(buf),
5260                                                  compose->account->name,
5261                                                  strlen("From: "),
5262                                                  FALSE);
5263                                         err |= (fprintf(fdest, "%s <%s>",
5264                                                 buf,
5265                                                 compose->account->address) < 0);
5266                                 } else
5267                                         err |= (fprintf(fdest, "%s",
5268                                                 compose->account->address) < 0);
5269                                 err |= (fputs(")", fdest) == EOF);
5270                         }
5271                 }
5272
5273                 if (fputs("\n", fdest) == -1)
5274                         goto error;
5275         }
5276
5277         if (err)
5278                 goto error;
5279
5280         if (compose_redirect_write_headers(compose, fdest))
5281                 goto error;
5282
5283         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5284                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5285                         goto error;
5286         }
5287
5288         fclose(fp);
5289
5290         return 0;
5291 error:
5292         fclose(fp);
5293
5294         return -1;
5295 }
5296
5297 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5298 {
5299         GtkTextBuffer *buffer;
5300         GtkTextIter start, end;
5301         gchar *chars;
5302         gchar *buf;
5303         const gchar *out_codeset;
5304         EncodingType encoding = ENC_UNKNOWN;
5305         MimeInfo *mimemsg, *mimetext;
5306         gint line;
5307         const gchar *src_codeset = CS_INTERNAL;
5308         gchar *from_addr = NULL;
5309         gchar *from_name = NULL;
5310
5311         if (action == COMPOSE_WRITE_FOR_SEND)
5312                 attach_parts = TRUE;
5313
5314         /* create message MimeInfo */
5315         mimemsg = procmime_mimeinfo_new();
5316         mimemsg->type = MIMETYPE_MESSAGE;
5317         mimemsg->subtype = g_strdup("rfc822");
5318         mimemsg->content = MIMECONTENT_MEM;
5319         mimemsg->tmp = TRUE; /* must free content later */
5320         mimemsg->data.mem = compose_get_header(compose);
5321
5322         /* Create text part MimeInfo */
5323         /* get all composed text */
5324         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5325         gtk_text_buffer_get_start_iter(buffer, &start);
5326         gtk_text_buffer_get_end_iter(buffer, &end);
5327         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5328
5329         out_codeset = conv_get_charset_str(compose->out_encoding);
5330
5331         if (!out_codeset && is_ascii_str(chars)) {
5332                 out_codeset = CS_US_ASCII;
5333         } else if (prefs_common.outgoing_fallback_to_ascii &&
5334                    is_ascii_str(chars)) {
5335                 out_codeset = CS_US_ASCII;
5336                 encoding = ENC_7BIT;
5337         }
5338
5339         if (!out_codeset) {
5340                 gchar *test_conv_global_out = NULL;
5341                 gchar *test_conv_reply = NULL;
5342
5343                 /* automatic mode. be automatic. */
5344                 codeconv_set_strict(TRUE);
5345
5346                 out_codeset = conv_get_outgoing_charset_str();
5347                 if (out_codeset) {
5348                         debug_print("trying to convert to %s\n", out_codeset);
5349                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5350                 }
5351
5352                 if (!test_conv_global_out && compose->orig_charset
5353                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5354                         out_codeset = compose->orig_charset;
5355                         debug_print("failure; trying to convert to %s\n", out_codeset);
5356                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5357                 }
5358
5359                 if (!test_conv_global_out && !test_conv_reply) {
5360                         /* we're lost */
5361                         out_codeset = CS_INTERNAL;
5362                         debug_print("failure; finally using %s\n", out_codeset);
5363                 }
5364                 g_free(test_conv_global_out);
5365                 g_free(test_conv_reply);
5366                 codeconv_set_strict(FALSE);
5367         }
5368
5369         if (encoding == ENC_UNKNOWN) {
5370                 if (prefs_common.encoding_method == CTE_BASE64)
5371                         encoding = ENC_BASE64;
5372                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5373                         encoding = ENC_QUOTED_PRINTABLE;
5374                 else if (prefs_common.encoding_method == CTE_8BIT)
5375                         encoding = ENC_8BIT;
5376                 else
5377                         encoding = procmime_get_encoding_for_charset(out_codeset);
5378         }
5379
5380         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5381                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5382
5383         if (action == COMPOSE_WRITE_FOR_SEND) {
5384                 codeconv_set_strict(TRUE);
5385                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5386                 codeconv_set_strict(FALSE);
5387
5388                 if (!buf) {
5389                         AlertValue aval;
5390                         gchar *msg;
5391
5392                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5393                                                 "to the specified %s charset.\n"
5394                                                 "Send it as %s?"), out_codeset, src_codeset);
5395                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5396                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5397                         g_free(msg);
5398
5399                         if (aval != G_ALERTALTERNATE) {
5400                                 g_free(chars);
5401                                 return -3;
5402                         } else {
5403                                 buf = chars;
5404                                 out_codeset = src_codeset;
5405                                 chars = NULL;
5406                         }
5407                 }
5408         } else {
5409                 buf = chars;
5410                 out_codeset = src_codeset;
5411                 chars = NULL;
5412         }
5413         g_free(chars);
5414
5415         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5416                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5417                     strstr(buf, "\nFrom ") != NULL) {
5418                         encoding = ENC_QUOTED_PRINTABLE;
5419                 }
5420         }
5421
5422         mimetext = procmime_mimeinfo_new();
5423         mimetext->content = MIMECONTENT_MEM;
5424         mimetext->tmp = TRUE; /* must free content later */
5425         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5426          * and free the data, which we need later. */
5427         mimetext->data.mem = g_strdup(buf); 
5428         mimetext->type = MIMETYPE_TEXT;
5429         mimetext->subtype = g_strdup("plain");
5430         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5431                             g_strdup(out_codeset));
5432                             
5433         /* protect trailing spaces when signing message */
5434         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5435             privacy_system_can_sign(compose->privacy_system)) {
5436                 encoding = ENC_QUOTED_PRINTABLE;
5437         }
5438         
5439         debug_print("main text: %zd bytes encoded as %s in %d\n",
5440                 strlen(buf), out_codeset, encoding);
5441
5442         /* check for line length limit */
5443         if (action == COMPOSE_WRITE_FOR_SEND &&
5444             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5445             check_line_length(buf, 1000, &line) < 0) {
5446                 AlertValue aval;
5447                 gchar *msg;
5448
5449                 msg = g_strdup_printf
5450                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5451                            "The contents of the message might be broken on the way to the delivery.\n"
5452                            "\n"
5453                            "Send it anyway?"), line + 1);
5454                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5455                 g_free(msg);
5456                 if (aval != G_ALERTALTERNATE) {
5457                         g_free(buf);
5458                         return -1;
5459                 }
5460         }
5461         
5462         if (encoding != ENC_UNKNOWN)
5463                 procmime_encode_content(mimetext, encoding);
5464
5465         /* append attachment parts */
5466         if (compose_use_attach(compose) && attach_parts) {
5467                 MimeInfo *mimempart;
5468                 gchar *boundary = NULL;
5469                 mimempart = procmime_mimeinfo_new();
5470                 mimempart->content = MIMECONTENT_EMPTY;
5471                 mimempart->type = MIMETYPE_MULTIPART;
5472                 mimempart->subtype = g_strdup("mixed");
5473
5474                 do {
5475                         g_free(boundary);
5476                         boundary = generate_mime_boundary(NULL);
5477                 } while (strstr(buf, boundary) != NULL);
5478
5479                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5480                                     boundary);
5481
5482                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5483
5484                 g_node_append(mimempart->node, mimetext->node);
5485                 g_node_append(mimemsg->node, mimempart->node);
5486
5487                 if (compose_add_attachments(compose, mimempart) < 0)
5488                         return -1;
5489         } else
5490                 g_node_append(mimemsg->node, mimetext->node);
5491
5492         g_free(buf);
5493
5494         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5495                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5496                 /* extract name and address */
5497                 if (strstr(spec, " <") && strstr(spec, ">")) {
5498                         from_addr = g_strdup(strrchr(spec, '<')+1);
5499                         *(strrchr(from_addr, '>')) = '\0';
5500                         from_name = g_strdup(spec);
5501                         *(strrchr(from_name, '<')) = '\0';
5502                 } else {
5503                         from_name = NULL;
5504                         from_addr = NULL;
5505                 }
5506                 g_free(spec);
5507         }
5508         /* sign message if sending */
5509         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5510             privacy_system_can_sign(compose->privacy_system))
5511                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5512                         compose->account, from_addr)) {
5513                         g_free(from_name);
5514                         g_free(from_addr);
5515                         return -2;
5516         }
5517         g_free(from_name);
5518         g_free(from_addr);
5519         procmime_write_mimeinfo(mimemsg, fp);
5520         
5521         procmime_mimeinfo_free_all(mimemsg);
5522
5523         return 0;
5524 }
5525
5526 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5527 {
5528         GtkTextBuffer *buffer;
5529         GtkTextIter start, end;
5530         FILE *fp;
5531         size_t len;
5532         gchar *chars, *tmp;
5533
5534         if ((fp = g_fopen(file, "wb")) == NULL) {
5535                 FILE_OP_ERROR(file, "fopen");
5536                 return -1;
5537         }
5538
5539         /* chmod for security */
5540         if (change_file_mode_rw(fp, file) < 0) {
5541                 FILE_OP_ERROR(file, "chmod");
5542                 g_warning("can't change file mode\n");
5543         }
5544
5545         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5546         gtk_text_buffer_get_start_iter(buffer, &start);
5547         gtk_text_buffer_get_end_iter(buffer, &end);
5548         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5549
5550         chars = conv_codeset_strdup
5551                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5552
5553         g_free(tmp);
5554         if (!chars) return -1;
5555
5556         /* write body */
5557         len = strlen(chars);
5558         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5559                 FILE_OP_ERROR(file, "fwrite");
5560                 g_free(chars);
5561                 fclose(fp);
5562                 claws_unlink(file);
5563                 return -1;
5564         }
5565
5566         g_free(chars);
5567
5568         if (fclose(fp) == EOF) {
5569                 FILE_OP_ERROR(file, "fclose");
5570                 claws_unlink(file);
5571                 return -1;
5572         }
5573         return 0;
5574 }
5575
5576 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5577 {
5578         FolderItem *item;
5579         MsgInfo *msginfo = compose->targetinfo;
5580
5581         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5582         if (!msginfo) return -1;
5583
5584         if (!force && MSG_IS_LOCKED(msginfo->flags))
5585                 return 0;
5586
5587         item = msginfo->folder;
5588         cm_return_val_if_fail(item != NULL, -1);
5589
5590         if (procmsg_msg_exist(msginfo) &&
5591             (folder_has_parent_of_type(item, F_QUEUE) ||
5592              folder_has_parent_of_type(item, F_DRAFT) 
5593              || msginfo == compose->autosaved_draft)) {
5594                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5595                         g_warning("can't remove the old message\n");
5596                         return -1;
5597                 } else {
5598                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5599                 }
5600         }
5601
5602         return 0;
5603 }
5604
5605 static void compose_remove_draft(Compose *compose)
5606 {
5607         FolderItem *drafts;
5608         MsgInfo *msginfo = compose->targetinfo;
5609         drafts = account_get_special_folder(compose->account, F_DRAFT);
5610
5611         if (procmsg_msg_exist(msginfo)) {
5612                 folder_item_remove_msg(drafts, msginfo->msgnum);
5613         }
5614
5615 }
5616
5617 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5618                    gboolean remove_reedit_target)
5619 {
5620         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5621 }
5622
5623 static gboolean compose_warn_encryption(Compose *compose)
5624 {
5625         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5626         AlertValue val = G_ALERTALTERNATE;
5627         
5628         if (warning == NULL)
5629                 return TRUE;
5630
5631         val = alertpanel_full(_("Encryption warning"), warning,
5632                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5633                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5634         if (val & G_ALERTDISABLE) {
5635                 val &= ~G_ALERTDISABLE;
5636                 if (val == G_ALERTALTERNATE)
5637                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5638                                 TRUE);
5639         }
5640
5641         if (val == G_ALERTALTERNATE) {
5642                 return TRUE;
5643         } else {
5644                 return FALSE;
5645         } 
5646 }
5647
5648 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5649                               gchar **msgpath, gboolean check_subject,
5650                               gboolean remove_reedit_target)
5651 {
5652         FolderItem *queue;
5653         gchar *tmp;
5654         FILE *fp;
5655         GSList *cur;
5656         gint num;
5657         static gboolean lock = FALSE;
5658         PrefsAccount *mailac = NULL, *newsac = NULL;
5659         gboolean err = FALSE;
5660
5661         debug_print("queueing message...\n");
5662         cm_return_val_if_fail(compose->account != NULL, -1);
5663
5664         lock = TRUE;
5665         
5666         if (compose_check_entries(compose, check_subject) == FALSE) {
5667                 lock = FALSE;
5668                 if (compose->batch) {
5669                         gtk_widget_show_all(compose->window);
5670                 }
5671                 return -1;
5672         }
5673
5674         if (!compose->to_list && !compose->newsgroup_list) {
5675                 g_warning("can't get recipient list.");
5676                 lock = FALSE;
5677                 return -1;
5678         }
5679
5680         if (compose->to_list) {
5681                 if (compose->account->protocol != A_NNTP)
5682                         mailac = compose->account;
5683                 else if (cur_account && cur_account->protocol != A_NNTP)
5684                         mailac = cur_account;
5685                 else if (!(mailac = compose_current_mail_account())) {
5686                         lock = FALSE;
5687                         alertpanel_error(_("No account for sending mails available!"));
5688                         return -1;
5689                 }
5690         }
5691
5692         if (compose->newsgroup_list) {
5693                 if (compose->account->protocol == A_NNTP)
5694                         newsac = compose->account;
5695                 else {
5696                         lock = FALSE;
5697                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5698                         return -1;
5699                 }                       
5700         }
5701
5702         /* write queue header */
5703         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5704                               G_DIR_SEPARATOR, compose, (guint) rand());
5705         debug_print("queuing to %s\n", tmp);
5706         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5707                 FILE_OP_ERROR(tmp, "fopen");
5708                 g_free(tmp);
5709                 lock = FALSE;
5710                 return -2;
5711         }
5712
5713         if (change_file_mode_rw(fp, tmp) < 0) {
5714                 FILE_OP_ERROR(tmp, "chmod");
5715                 g_warning("can't change file mode\n");
5716         }
5717
5718         /* queueing variables */
5719         err |= (fprintf(fp, "AF:\n") < 0);
5720         err |= (fprintf(fp, "NF:0\n") < 0);
5721         err |= (fprintf(fp, "PS:10\n") < 0);
5722         err |= (fprintf(fp, "SRH:1\n") < 0);
5723         err |= (fprintf(fp, "SFN:\n") < 0);
5724         err |= (fprintf(fp, "DSR:\n") < 0);
5725         if (compose->msgid)
5726                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5727         else
5728                 err |= (fprintf(fp, "MID:\n") < 0);
5729         err |= (fprintf(fp, "CFG:\n") < 0);
5730         err |= (fprintf(fp, "PT:0\n") < 0);
5731         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5732         err |= (fprintf(fp, "RQ:\n") < 0);
5733         if (mailac)
5734                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5735         else
5736                 err |= (fprintf(fp, "SSV:\n") < 0);
5737         if (newsac)
5738                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5739         else
5740                 err |= (fprintf(fp, "NSV:\n") < 0);
5741         err |= (fprintf(fp, "SSH:\n") < 0);
5742         /* write recepient list */
5743         if (compose->to_list) {
5744                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5745                 for (cur = compose->to_list->next; cur != NULL;
5746                      cur = cur->next)
5747                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5748                 err |= (fprintf(fp, "\n") < 0);
5749         }
5750         /* write newsgroup list */
5751         if (compose->newsgroup_list) {
5752                 err |= (fprintf(fp, "NG:") < 0);
5753                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5754                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5755                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5756                 err |= (fprintf(fp, "\n") < 0);
5757         }
5758         /* Sylpheed account IDs */
5759         if (mailac)
5760                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5761         if (newsac)
5762                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5763
5764         
5765         if (compose->privacy_system != NULL) {
5766                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5767                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5768                 if (compose->use_encryption) {
5769                         gchar *encdata;
5770                         if (!compose_warn_encryption(compose)) {
5771                                 lock = FALSE;
5772                                 fclose(fp);
5773                                 claws_unlink(tmp);
5774                                 g_free(tmp);
5775                                 return -6;
5776                         }
5777                         if (mailac && mailac->encrypt_to_self) {
5778                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5779                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5780                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5781                                 g_slist_free(tmp_list);
5782                         } else {
5783                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5784                         }
5785                         if (encdata != NULL) {
5786                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5787                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5788                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5789                                                 encdata) < 0);
5790                                 } /* else we finally dont want to encrypt */
5791                         } else {
5792                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5793                                 /* and if encdata was null, it means there's been a problem in 
5794                                  * key selection */
5795                                 lock = FALSE;
5796                                 fclose(fp);
5797                                 claws_unlink(tmp);
5798                                 g_free(tmp);
5799                                 return -5;
5800                         }
5801                         g_free(encdata);
5802                 }
5803         }
5804
5805         /* Save copy folder */
5806         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5807                 gchar *savefolderid;
5808                 
5809                 savefolderid = compose_get_save_to(compose);
5810                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5811                 g_free(savefolderid);
5812         }
5813         /* Save copy folder */
5814         if (compose->return_receipt) {
5815                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5816         }
5817         /* Message-ID of message replying to */
5818         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5819                 gchar *folderid;
5820                 
5821                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5822                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5823                 g_free(folderid);
5824         }
5825         /* Message-ID of message forwarding to */
5826         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5827                 gchar *folderid;
5828                 
5829                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5830                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5831                 g_free(folderid);
5832         }
5833
5834         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5835         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5836
5837         /* end of headers */
5838         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5839
5840         if (compose->redirect_filename != NULL) {
5841                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5842                         lock = FALSE;
5843                         fclose(fp);
5844                         claws_unlink(tmp);
5845                         g_free(tmp);
5846                         return -2;
5847                 }
5848         } else {
5849                 gint result = 0;
5850                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5851                         lock = FALSE;
5852                         fclose(fp);
5853                         claws_unlink(tmp);
5854                         g_free(tmp);
5855                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5856                 }
5857         }
5858         if (err == TRUE) {
5859                 g_warning("failed to write queue message\n");
5860                 fclose(fp);
5861                 claws_unlink(tmp);
5862                 g_free(tmp);
5863                 lock = FALSE;
5864                 return -2;
5865         }
5866         if (fclose(fp) == EOF) {
5867                 FILE_OP_ERROR(tmp, "fclose");
5868                 claws_unlink(tmp);
5869                 g_free(tmp);
5870                 lock = FALSE;
5871                 return -2;
5872         }
5873
5874         if (item && *item) {
5875                 queue = *item;
5876         } else {
5877                 queue = account_get_special_folder(compose->account, F_QUEUE);
5878         }
5879         if (!queue) {
5880                 g_warning("can't find queue folder\n");
5881                 claws_unlink(tmp);
5882                 g_free(tmp);
5883                 lock = FALSE;
5884                 return -1;
5885         }
5886         folder_item_scan(queue);
5887         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5888                 g_warning("can't queue the message\n");
5889                 claws_unlink(tmp);
5890                 g_free(tmp);
5891                 lock = FALSE;
5892                 return -1;
5893         }
5894         
5895         if (msgpath == NULL) {
5896                 claws_unlink(tmp);
5897                 g_free(tmp);
5898         } else
5899                 *msgpath = tmp;
5900
5901         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5902                 compose_remove_reedit_target(compose, FALSE);
5903         }
5904
5905         if ((msgnum != NULL) && (item != NULL)) {
5906                 *msgnum = num;
5907                 *item = queue;
5908         }
5909
5910         return 0;
5911 }
5912
5913 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5914 {
5915         AttachInfo *ainfo;
5916         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5917         MimeInfo *mimepart;
5918         struct stat statbuf;
5919         gchar *type, *subtype;
5920         GtkTreeModel *model;
5921         GtkTreeIter iter;
5922
5923         model = gtk_tree_view_get_model(tree_view);
5924         
5925         if (!gtk_tree_model_get_iter_first(model, &iter))
5926                 return 0;
5927         do {
5928                 gtk_tree_model_get(model, &iter,
5929                                    COL_DATA, &ainfo,
5930                                    -1);
5931                 
5932                 if (!is_file_exist(ainfo->file)) {
5933                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5934                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5935                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5936                         g_free(msg);
5937                         if (val == G_ALERTDEFAULT) {
5938                                 return -1;
5939                         }
5940                         continue;
5941                 }
5942                 mimepart = procmime_mimeinfo_new();
5943                 mimepart->content = MIMECONTENT_FILE;
5944                 mimepart->data.filename = g_strdup(ainfo->file);
5945                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5946                 mimepart->offset = 0;
5947
5948                 g_stat(ainfo->file, &statbuf);
5949                 mimepart->length = statbuf.st_size;
5950
5951                 type = g_strdup(ainfo->content_type);
5952
5953                 if (!strchr(type, '/')) {
5954                         g_free(type);
5955                         type = g_strdup("application/octet-stream");
5956                 }
5957
5958                 subtype = strchr(type, '/') + 1;
5959                 *(subtype - 1) = '\0';
5960                 mimepart->type = procmime_get_media_type(type);
5961                 mimepart->subtype = g_strdup(subtype);
5962                 g_free(type);
5963
5964                 if (mimepart->type == MIMETYPE_MESSAGE && 
5965                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5966                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5967                 } else {
5968                         if (ainfo->name) {
5969                                 if (mimepart->type == MIMETYPE_APPLICATION && 
5970                                    !strcmp2(mimepart->subtype, "octet-stream"))
5971                                         g_hash_table_insert(mimepart->typeparameters,
5972                                                     g_strdup("name"), g_strdup(ainfo->name));
5973                                 g_hash_table_insert(mimepart->dispositionparameters,
5974                                             g_strdup("filename"), g_strdup(ainfo->name));
5975                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5976                         }
5977                 }
5978
5979                 if (compose->use_signing) {
5980                         if (ainfo->encoding == ENC_7BIT)
5981                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5982                         else if (ainfo->encoding == ENC_8BIT)
5983                                 ainfo->encoding = ENC_BASE64;
5984                 }
5985                 
5986                 procmime_encode_content(mimepart, ainfo->encoding);
5987
5988                 g_node_append(parent->node, mimepart->node);
5989         } while (gtk_tree_model_iter_next(model, &iter));
5990         
5991         return 0;
5992 }
5993
5994 #define IS_IN_CUSTOM_HEADER(header) \
5995         (compose->account->add_customhdr && \
5996          custom_header_find(compose->account->customhdr_list, header) != NULL)
5997
5998 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5999                                                     GString *header, 
6000                                                     const gchar *fieldname,
6001                                                     const gchar *seperator)
6002 {
6003         gchar *str, *fieldname_w_colon;
6004         gboolean add_field = FALSE;
6005         GSList *list;
6006         ComposeHeaderEntry *headerentry;
6007         const gchar *headerentryname;
6008         const gchar *trans_fieldname;
6009         GString *fieldstr;
6010
6011         if (IS_IN_CUSTOM_HEADER(fieldname))
6012                 return;
6013
6014         debug_print("Adding %s-fields\n", fieldname);
6015
6016         fieldstr = g_string_sized_new(64);
6017
6018         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6019         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6020
6021         for (list = compose->header_list; list; list = list->next) {
6022                 headerentry = ((ComposeHeaderEntry *)list->data);
6023                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6024
6025                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6026                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6027                         g_strstrip(str);
6028                         if (str[0] != '\0') {
6029                                 if (add_field)
6030                                         g_string_append(fieldstr, seperator);
6031                                 g_string_append(fieldstr, str);
6032                                 add_field = TRUE;
6033                         }
6034                         g_free(str);
6035                 }
6036         }
6037         if (add_field) {
6038                 gchar *buf;
6039
6040                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6041                 compose_convert_header
6042                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6043                         strlen(fieldname) + 2, TRUE);
6044                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6045                 g_free(buf);
6046         }
6047
6048         g_free(fieldname_w_colon);
6049         g_string_free(fieldstr, TRUE);
6050
6051         return;
6052 }
6053
6054 static gchar *compose_get_header(Compose *compose)
6055 {
6056         gchar buf[BUFFSIZE];
6057         const gchar *entry_str;
6058         gchar *str;
6059         gchar *name;
6060         GSList *list;
6061         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6062         GString *header;
6063         gchar *from_name = NULL, *from_address = NULL;
6064         gchar *tmp;
6065
6066         cm_return_val_if_fail(compose->account != NULL, NULL);
6067         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6068
6069         header = g_string_sized_new(64);
6070
6071         /* Date */
6072         get_rfc822_date(buf, sizeof(buf));
6073         g_string_append_printf(header, "Date: %s\n", buf);
6074
6075         /* From */
6076         
6077         if (compose->account->name && *compose->account->name) {
6078                 gchar *buf;
6079                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6080                 tmp = g_strdup_printf("%s <%s>",
6081                         buf, compose->account->address);
6082         } else {
6083                 tmp = g_strdup_printf("%s",
6084                         compose->account->address);
6085         }
6086         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6087         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6088                 /* use default */
6089                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6090                 from_address = g_strdup(compose->account->address);
6091         } else {
6092                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6093                 /* extract name and address */
6094                 if (strstr(spec, " <") && strstr(spec, ">")) {
6095                         from_address = g_strdup(strrchr(spec, '<')+1);
6096                         *(strrchr(from_address, '>')) = '\0';
6097                         from_name = g_strdup(spec);
6098                         *(strrchr(from_name, '<')) = '\0';
6099                 } else {
6100                         from_name = NULL;
6101                         from_address = g_strdup(spec);
6102                 }
6103                 g_free(spec);
6104         }
6105         g_free(tmp);
6106         
6107         
6108         if (from_name && *from_name) {
6109                 compose_convert_header
6110                         (compose, buf, sizeof(buf), from_name,
6111                          strlen("From: "), TRUE);
6112                 QUOTE_IF_REQUIRED(name, buf);
6113                 
6114                 g_string_append_printf(header, "From: %s <%s>\n",
6115                         name, from_address);
6116         } else
6117                 g_string_append_printf(header, "From: %s\n", from_address);
6118         
6119         g_free(from_name);
6120         g_free(from_address);
6121
6122         /* To */
6123         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6124
6125         /* Newsgroups */
6126         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6127
6128         /* Cc */
6129         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6130
6131         /* Bcc */
6132         /* 
6133          * If this account is a NNTP account remove Bcc header from 
6134          * message body since it otherwise will be publicly shown
6135          */
6136         if (compose->account->protocol != A_NNTP)
6137                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6138
6139         /* Subject */
6140         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6141
6142         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6143                 g_strstrip(str);
6144                 if (*str != '\0') {
6145                         compose_convert_header(compose, buf, sizeof(buf), str,
6146                                                strlen("Subject: "), FALSE);
6147                         g_string_append_printf(header, "Subject: %s\n", buf);
6148                 }
6149         }
6150         g_free(str);
6151
6152         /* Message-ID */
6153         if (compose->account->set_domain && compose->account->domain) {
6154                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
6155         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6156                 g_snprintf(buf, sizeof(buf), "%s", 
6157                         strchr(compose->account->address, '@') ?
6158                                 strchr(compose->account->address, '@')+1 :
6159                                 compose->account->address);
6160         } else {
6161                 g_snprintf(buf, sizeof(buf), "%s", "");
6162         }
6163         
6164         if (compose->account->gen_msgid) {
6165                 gchar *addr = NULL;
6166                 if (compose->account->msgid_with_addr) {
6167                         addr = compose->account->address;
6168                 }
6169                 generate_msgid(buf, sizeof(buf), addr);
6170                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6171                 compose->msgid = g_strdup(buf);
6172         } else {
6173                 compose->msgid = NULL;
6174         }
6175
6176         if (compose->remove_references == FALSE) {
6177                 /* In-Reply-To */
6178                 if (compose->inreplyto && compose->to_list)
6179                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6180         
6181                 /* References */
6182                 if (compose->references)
6183                         g_string_append_printf(header, "References: %s\n", compose->references);
6184         }
6185
6186         /* Followup-To */
6187         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6188
6189         /* Reply-To */
6190         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6191
6192         /* Organization */
6193         if (compose->account->organization &&
6194             strlen(compose->account->organization) &&
6195             !IS_IN_CUSTOM_HEADER("Organization")) {
6196                 compose_convert_header(compose, buf, sizeof(buf),
6197                                        compose->account->organization,
6198                                        strlen("Organization: "), FALSE);
6199                 g_string_append_printf(header, "Organization: %s\n", buf);
6200         }
6201
6202         /* Program version and system info */
6203         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6204             !compose->newsgroup_list) {
6205                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6206                         prog_version,
6207                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6208                         TARGET_ALIAS);
6209         }
6210         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6211                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6212                         prog_version,
6213                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6214                         TARGET_ALIAS);
6215         }
6216
6217         /* custom headers */
6218         if (compose->account->add_customhdr) {
6219                 GSList *cur;
6220
6221                 for (cur = compose->account->customhdr_list; cur != NULL;
6222                      cur = cur->next) {
6223                         CustomHeader *chdr = (CustomHeader *)cur->data;
6224
6225                         if (custom_header_is_allowed(chdr->name)) {
6226                                 compose_convert_header
6227                                         (compose, buf, sizeof(buf),
6228                                          chdr->value ? chdr->value : "",
6229                                          strlen(chdr->name) + 2, FALSE);
6230                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6231                         }
6232                 }
6233         }
6234
6235         /* Automatic Faces and X-Faces */
6236         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6237                 g_string_append_printf(header, "X-Face: %s\n", buf);
6238         }
6239         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6240                 g_string_append_printf(header, "X-Face: %s\n", buf);
6241         }
6242         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6243                 g_string_append_printf(header, "Face: %s\n", buf);
6244         }
6245         else if (get_default_face (buf, sizeof(buf)) == 0) {
6246                 g_string_append_printf(header, "Face: %s\n", buf);
6247         }
6248
6249         /* PRIORITY */
6250         switch (compose->priority) {
6251                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6252                                                    "X-Priority: 1 (Highest)\n");
6253                         break;
6254                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6255                                                 "X-Priority: 2 (High)\n");
6256                         break;
6257                 case PRIORITY_NORMAL: break;
6258                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6259                                                "X-Priority: 4 (Low)\n");
6260                         break;
6261                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6262                                                   "X-Priority: 5 (Lowest)\n");
6263                         break;
6264                 default: debug_print("compose: priority unknown : %d\n",
6265                                      compose->priority);
6266         }
6267
6268         /* Request Return Receipt */
6269         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6270                 if (compose->return_receipt) {
6271                         if (compose->account->name
6272                             && *compose->account->name) {
6273                                 compose_convert_header(compose, buf, sizeof(buf), 
6274                                                        compose->account->name, 
6275                                                        strlen("Disposition-Notification-To: "),
6276                                                        TRUE);
6277                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6278                         } else
6279                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6280                 }
6281         }
6282
6283         /* get special headers */
6284         for (list = compose->header_list; list; list = list->next) {
6285                 ComposeHeaderEntry *headerentry;
6286                 gchar *tmp;
6287                 gchar *headername;
6288                 gchar *headername_wcolon;
6289                 const gchar *headername_trans;
6290                 gchar *headervalue;
6291                 gchar **string;
6292                 gboolean standard_header = FALSE;
6293
6294                 headerentry = ((ComposeHeaderEntry *)list->data);
6295
6296                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6297                 g_strstrip(tmp);
6298                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6299                         g_free(tmp);
6300                         continue;
6301                 }
6302
6303                 if (!strstr(tmp, ":")) {
6304                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6305                         headername = g_strdup(tmp);
6306                 } else {
6307                         headername_wcolon = g_strdup(tmp);
6308                         headername = g_strdup(strtok(tmp, ":"));
6309                 }
6310                 g_free(tmp);
6311                 
6312                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6313                 Xstrdup_a(headervalue, entry_str, return NULL);
6314                 subst_char(headervalue, '\r', ' ');
6315                 subst_char(headervalue, '\n', ' ');
6316                 string = std_headers;
6317                 while (*string != NULL) {
6318                         headername_trans = prefs_common_translated_header_name(*string);
6319                         if (!strcmp(headername_trans, headername_wcolon))
6320                                 standard_header = TRUE;
6321                         string++;
6322                 }
6323                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6324                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6325                                 
6326                 g_free(headername);
6327                 g_free(headername_wcolon);              
6328         }
6329
6330         str = header->str;
6331         g_string_free(header, FALSE);
6332
6333         return str;
6334 }
6335
6336 #undef IS_IN_CUSTOM_HEADER
6337
6338 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6339                                    gint header_len, gboolean addr_field)
6340 {
6341         gchar *tmpstr = NULL;
6342         const gchar *out_codeset = NULL;
6343
6344         cm_return_if_fail(src != NULL);
6345         cm_return_if_fail(dest != NULL);
6346
6347         if (len < 1) return;
6348
6349         tmpstr = g_strdup(src);
6350
6351         subst_char(tmpstr, '\n', ' ');
6352         subst_char(tmpstr, '\r', ' ');
6353         g_strchomp(tmpstr);
6354
6355         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6356                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6357                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6358                 g_free(tmpstr);
6359                 tmpstr = mybuf;
6360         }
6361
6362         codeconv_set_strict(TRUE);
6363         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6364                 conv_get_charset_str(compose->out_encoding));
6365         codeconv_set_strict(FALSE);
6366         
6367         if (!dest || *dest == '\0') {
6368                 gchar *test_conv_global_out = NULL;
6369                 gchar *test_conv_reply = NULL;
6370
6371                 /* automatic mode. be automatic. */
6372                 codeconv_set_strict(TRUE);
6373
6374                 out_codeset = conv_get_outgoing_charset_str();
6375                 if (out_codeset) {
6376                         debug_print("trying to convert to %s\n", out_codeset);
6377                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6378                 }
6379
6380                 if (!test_conv_global_out && compose->orig_charset
6381                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6382                         out_codeset = compose->orig_charset;
6383                         debug_print("failure; trying to convert to %s\n", out_codeset);
6384                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6385                 }
6386
6387                 if (!test_conv_global_out && !test_conv_reply) {
6388                         /* we're lost */
6389                         out_codeset = CS_INTERNAL;
6390                         debug_print("finally using %s\n", out_codeset);
6391                 }
6392                 g_free(test_conv_global_out);
6393                 g_free(test_conv_reply);
6394                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6395                                         out_codeset);
6396                 codeconv_set_strict(FALSE);
6397         }
6398         g_free(tmpstr);
6399 }
6400
6401 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6402 {
6403         gchar *address;
6404
6405         cm_return_if_fail(user_data != NULL);
6406
6407         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6408         g_strstrip(address);
6409         if (*address != '\0') {
6410                 gchar *name = procheader_get_fromname(address);
6411                 extract_address(address);
6412                 addressbook_add_contact(name, address, NULL, NULL);
6413         }
6414         g_free(address);
6415 }
6416
6417 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6418 {
6419         GtkWidget *menuitem;
6420         gchar *address;
6421
6422         cm_return_if_fail(menu != NULL);
6423         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6424
6425         menuitem = gtk_separator_menu_item_new();
6426         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6427         gtk_widget_show(menuitem);
6428
6429         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6430         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6431
6432         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6433         g_strstrip(address);
6434         if (*address == '\0') {
6435                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6436         }
6437
6438         g_signal_connect(G_OBJECT(menuitem), "activate",
6439                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6440         gtk_widget_show(menuitem);
6441 }
6442
6443 static void compose_create_header_entry(Compose *compose) 
6444 {
6445         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6446
6447         GtkWidget *combo;
6448         GtkWidget *entry;
6449         GtkWidget *button;
6450         GtkWidget *hbox;
6451         gchar **string;
6452         const gchar *header = NULL;
6453         ComposeHeaderEntry *headerentry;
6454         gboolean standard_header = FALSE;
6455         GtkListStore *model;
6456         GtkTreeIter iter;
6457 #if !(GTK_CHECK_VERSION(2,12,0))
6458         GtkTooltips *tips = compose->tooltips;
6459 #endif
6460         
6461         headerentry = g_new0(ComposeHeaderEntry, 1);
6462
6463         /* Combo box */
6464         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6465         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6466         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6467                         COMPOSE_TO);
6468         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6469                         COMPOSE_CC);
6470         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6471                         COMPOSE_BCC);
6472         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6473                         COMPOSE_NEWSGROUPS);                    
6474         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6475                         COMPOSE_REPLYTO);
6476         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6477                         COMPOSE_FOLLOWUPTO);
6478
6479         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6480         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6481                          G_CALLBACK(compose_grab_focus_cb), compose);
6482         gtk_widget_show(combo);
6483         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6484                         compose->header_nextrow, compose->header_nextrow+1,
6485                         GTK_SHRINK, GTK_FILL, 0, 0);
6486         if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6487                 const gchar *last_header_entry = gtk_entry_get_text(
6488                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6489                 string = headers;
6490                 while (*string != NULL) {
6491                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6492                                 standard_header = TRUE;
6493                         string++;
6494                 }
6495                 if (standard_header)
6496                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6497         }
6498         if (!compose->header_last || !standard_header) {
6499                 switch(compose->account->protocol) {
6500                         case A_NNTP:
6501                                 header = prefs_common_translated_header_name("Newsgroups:");
6502                                 break;
6503                         default:
6504                                 header = prefs_common_translated_header_name("To:");
6505                                 break;
6506                 }                                                                   
6507         }
6508         if (header)
6509                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6510
6511         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6512                          G_CALLBACK(compose_grab_focus_cb), compose);
6513
6514         /* Entry field with cleanup button */
6515         button = gtk_button_new();
6516         gtk_button_set_image(GTK_BUTTON(button),
6517                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6518         gtk_widget_show(button);
6519         CLAWS_SET_TIP(button,
6520                 _("Delete entry contents"));
6521         entry = gtk_entry_new(); 
6522         gtk_widget_show(entry);
6523         CLAWS_SET_TIP(entry,
6524                 _("Use <tab> to autocomplete from addressbook"));
6525         hbox = gtk_hbox_new (FALSE, 0);
6526         gtk_widget_show(hbox);
6527         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6528         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6529         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6530                         compose->header_nextrow, compose->header_nextrow+1,
6531                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6532
6533         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6534                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6535                          headerentry);
6536         g_signal_connect(G_OBJECT(entry), "changed", 
6537                          G_CALLBACK(compose_headerentry_changed_cb), 
6538                          headerentry);
6539         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6540                          G_CALLBACK(compose_grab_focus_cb), compose);
6541
6542         g_signal_connect(G_OBJECT(button), "clicked",
6543                          G_CALLBACK(compose_headerentry_button_clicked_cb),
6544                          headerentry); 
6545                          
6546         /* email dnd */
6547         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6548                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6549                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6550         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6551                          G_CALLBACK(compose_header_drag_received_cb),
6552                          entry);
6553         g_signal_connect(G_OBJECT(entry), "drag-drop",
6554                          G_CALLBACK(compose_drag_drop),
6555                          compose);
6556         g_signal_connect(G_OBJECT(entry), "populate-popup",
6557                          G_CALLBACK(compose_entry_popup_extend),
6558                          NULL);
6559         
6560         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6561
6562         headerentry->compose = compose;
6563         headerentry->combo = combo;
6564         headerentry->entry = entry;
6565         headerentry->button = button;
6566         headerentry->hbox = hbox;
6567         headerentry->headernum = compose->header_nextrow;
6568         headerentry->type = PREF_NONE;
6569
6570         compose->header_nextrow++;
6571         compose->header_last = headerentry;             
6572         compose->header_list =
6573                 g_slist_append(compose->header_list,
6574                                headerentry);
6575 }
6576
6577 static void compose_add_header_entry(Compose *compose, const gchar *header,
6578                                 gchar *text, ComposePrefType pref_type) 
6579 {
6580         ComposeHeaderEntry *last_header = compose->header_last;
6581         gchar *tmp = g_strdup(text), *email;
6582         gboolean replyto_hdr;
6583         
6584         replyto_hdr = (!strcasecmp(header,
6585                                 prefs_common_translated_header_name("Reply-To:")) ||
6586                         !strcasecmp(header,
6587                                 prefs_common_translated_header_name("Followup-To:")) ||
6588                         !strcasecmp(header,
6589                                 prefs_common_translated_header_name("In-Reply-To:")));
6590                 
6591         extract_address(tmp);
6592         email = g_utf8_strdown(tmp, -1);
6593         
6594         if (replyto_hdr == FALSE &&
6595             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6596         {
6597                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6598                                 header, text, (gint) pref_type);
6599                 g_free(email);
6600                 g_free(tmp);
6601                 return;
6602         }
6603         
6604         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6605                 gtk_entry_set_text(GTK_ENTRY(
6606                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6607         else
6608                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6609         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6610         last_header->type = pref_type;
6611
6612         if (replyto_hdr == FALSE)
6613                 g_hash_table_insert(compose->email_hashtable, email,
6614                                     GUINT_TO_POINTER(1));
6615         else
6616                 g_free(email);
6617         
6618         g_free(tmp);
6619 }
6620
6621 static void compose_destroy_headerentry(Compose *compose, 
6622                                         ComposeHeaderEntry *headerentry)
6623 {
6624         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6625         gchar *email;
6626
6627         extract_address(text);
6628         email = g_utf8_strdown(text, -1);
6629         g_hash_table_remove(compose->email_hashtable, email);
6630         g_free(text);
6631         g_free(email);
6632         
6633         gtk_widget_destroy(headerentry->combo);
6634         gtk_widget_destroy(headerentry->entry);
6635         gtk_widget_destroy(headerentry->button);
6636         gtk_widget_destroy(headerentry->hbox);
6637         g_free(headerentry);
6638 }
6639
6640 static void compose_remove_header_entries(Compose *compose) 
6641 {
6642         GSList *list;
6643         for (list = compose->header_list; list; list = list->next)
6644                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6645
6646         compose->header_last = NULL;
6647         g_slist_free(compose->header_list);
6648         compose->header_list = NULL;
6649         compose->header_nextrow = 1;
6650         compose_create_header_entry(compose);
6651 }
6652
6653 static GtkWidget *compose_create_header(Compose *compose) 
6654 {
6655         GtkWidget *from_optmenu_hbox;
6656         GtkWidget *header_scrolledwin;
6657         GtkWidget *header_table;
6658
6659         gint count = 0;
6660
6661         /* header labels and entries */
6662         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6663         gtk_widget_show(header_scrolledwin);
6664         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6665
6666         header_table = gtk_table_new(2, 2, FALSE);
6667         gtk_widget_show(header_table);
6668         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6669         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6670         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6671         count = 0;
6672
6673         /* option menu for selecting accounts */
6674         from_optmenu_hbox = compose_account_option_menu_create(compose);
6675         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6676                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6677         count++;
6678
6679         compose->header_table = header_table;
6680         compose->header_list = NULL;
6681         compose->header_nextrow = count;
6682
6683         compose_create_header_entry(compose);
6684
6685         compose->table            = NULL;
6686
6687         return header_scrolledwin ;
6688 }
6689
6690 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6691 {
6692         Compose *compose = (Compose *)data;
6693         GdkEventButton event;
6694         
6695         event.button = 3;
6696         event.time = gtk_get_current_event_time();
6697
6698         return attach_button_pressed(compose->attach_clist, &event, compose);
6699 }
6700
6701 static GtkWidget *compose_create_attach(Compose *compose)
6702 {
6703         GtkWidget *attach_scrwin;
6704         GtkWidget *attach_clist;
6705
6706         GtkListStore *store;
6707         GtkCellRenderer *renderer;
6708         GtkTreeViewColumn *column;
6709         GtkTreeSelection *selection;
6710
6711         /* attachment list */
6712         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6713         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6714                                        GTK_POLICY_AUTOMATIC,
6715                                        GTK_POLICY_AUTOMATIC);
6716         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6717
6718         store = gtk_list_store_new(N_ATTACH_COLS, 
6719                                    G_TYPE_STRING,
6720                                    G_TYPE_STRING,
6721                                    G_TYPE_STRING,
6722                                    G_TYPE_POINTER,
6723                                    G_TYPE_AUTO_POINTER,
6724                                    -1);
6725         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6726                                         (GTK_TREE_MODEL(store)));
6727         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6728         g_object_unref(store);
6729         
6730         renderer = gtk_cell_renderer_text_new();
6731         column = gtk_tree_view_column_new_with_attributes
6732                         (_("Mime type"), renderer, "text", 
6733                          COL_MIMETYPE, NULL);
6734         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6735         
6736         renderer = gtk_cell_renderer_text_new();
6737         column = gtk_tree_view_column_new_with_attributes
6738                         (_("Size"), renderer, "text", 
6739                          COL_SIZE, NULL);
6740         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6741         
6742         renderer = gtk_cell_renderer_text_new();
6743         column = gtk_tree_view_column_new_with_attributes
6744                         (_("Name"), renderer, "text", 
6745                          COL_NAME, NULL);
6746         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6747
6748         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6749                                      prefs_common.use_stripes_everywhere);
6750         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6751         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6752
6753         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6754                          G_CALLBACK(attach_selected), compose);
6755         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6756                          G_CALLBACK(attach_button_pressed), compose);
6757 #ifndef MAEMO
6758         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6759                          G_CALLBACK(popup_attach_button_pressed), compose);
6760 #else
6761         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6762                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6763         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6764                          G_CALLBACK(popup_attach_button_pressed), compose);
6765 #endif
6766         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6767                          G_CALLBACK(attach_key_pressed), compose);
6768
6769         /* drag and drop */
6770         gtk_drag_dest_set(attach_clist,
6771                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6772                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6773                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6774         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6775                          G_CALLBACK(compose_attach_drag_received_cb),
6776                          compose);
6777         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6778                          G_CALLBACK(compose_drag_drop),
6779                          compose);
6780
6781         compose->attach_scrwin = attach_scrwin;
6782         compose->attach_clist  = attach_clist;
6783
6784         return attach_scrwin;
6785 }
6786
6787 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6788 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6789
6790 static GtkWidget *compose_create_others(Compose *compose)
6791 {
6792         GtkWidget *table;
6793         GtkWidget *savemsg_checkbtn;
6794         GtkWidget *savemsg_combo;
6795         GtkWidget *savemsg_select;
6796         
6797         guint rowcount = 0;
6798         gchar *folderidentifier;
6799
6800         /* Table for settings */
6801         table = gtk_table_new(3, 1, FALSE);
6802         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6803         gtk_widget_show(table);
6804         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6805         rowcount = 0;
6806
6807         /* Save Message to folder */
6808         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6809         gtk_widget_show(savemsg_checkbtn);
6810         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6811         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6812                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6813         }
6814         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6815                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6816
6817         savemsg_combo = gtk_combo_box_entry_new_text();
6818         compose->savemsg_checkbtn = savemsg_checkbtn;
6819         compose->savemsg_combo = savemsg_combo;
6820         gtk_widget_show(savemsg_combo);
6821
6822         if (prefs_common.compose_save_to_history)
6823                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6824                                 prefs_common.compose_save_to_history);
6825
6826         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6827         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6828         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6829                          G_CALLBACK(compose_grab_focus_cb), compose);
6830         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6831                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6832                                   (compose->account, F_OUTBOX));
6833                 compose_set_save_to(compose, folderidentifier);
6834                 g_free(folderidentifier);
6835         }
6836
6837         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6838         gtk_widget_show(savemsg_select);
6839         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6840         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6841                          G_CALLBACK(compose_savemsg_select_cb),
6842                          compose);
6843
6844         rowcount++;
6845
6846         return table;   
6847 }
6848
6849 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6850 {
6851         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6852                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6853 }
6854
6855 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6856 {
6857         FolderItem *dest;
6858         gchar * path;
6859
6860         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6861         if (!dest) return;
6862
6863         path = folder_item_get_identifier(dest);
6864
6865         compose_set_save_to(compose, path);
6866         g_free(path);
6867 }
6868
6869 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6870                                   GdkAtom clip, GtkTextIter *insert_place);
6871
6872
6873 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6874                                        Compose *compose)
6875 {
6876         gint prev_autowrap;
6877         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6878 #if USE_ENCHANT
6879         if (event->button == 3) {
6880                 GtkTextIter iter;
6881                 GtkTextIter sel_start, sel_end;
6882                 gboolean stuff_selected;
6883                 gint x, y;
6884                 /* move the cursor to allow GtkAspell to check the word
6885                  * under the mouse */
6886                 if (event->x && event->y) {
6887                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6888                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6889                                 &x, &y);
6890                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6891                                 &iter, x, y);
6892                 } else {
6893                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6894                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6895                 }
6896                 /* get selection */
6897                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6898                                 buffer,
6899                                 &sel_start, &sel_end);
6900
6901                 gtk_text_buffer_place_cursor (buffer, &iter);
6902                 /* reselect stuff */
6903                 if (stuff_selected 
6904                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6905                         gtk_text_buffer_select_range(buffer,
6906                                 &sel_start, &sel_end);
6907                 }
6908                 return FALSE; /* pass the event so that the right-click goes through */
6909         }
6910 #endif
6911         if (event->button == 2) {
6912                 GtkTextIter iter;
6913                 gint x, y;
6914                 BLOCK_WRAP();
6915                 
6916                 /* get the middle-click position to paste at the correct place */
6917                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6918                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6919                         &x, &y);
6920                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6921                         &iter, x, y);
6922                 
6923                 entry_paste_clipboard(compose, text, 
6924                                 prefs_common.linewrap_pastes,
6925                                 GDK_SELECTION_PRIMARY, &iter);
6926                 UNBLOCK_WRAP();
6927                 return TRUE;
6928         }
6929         return FALSE;
6930 }
6931
6932 #if USE_ENCHANT
6933 static void compose_spell_menu_changed(void *data)
6934 {
6935         Compose *compose = (Compose *)data;
6936         GSList *items;
6937         GtkWidget *menuitem;
6938         GtkWidget *parent_item;
6939         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6940         GSList *spell_menu;
6941
6942         if (compose->gtkaspell == NULL)
6943                 return;
6944
6945         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
6946                         "/Menu/Spelling/Options");
6947
6948         /* setting the submenu removes /Spelling/Options from the factory 
6949          * so we need to save it */
6950
6951         if (parent_item == NULL) {
6952                 parent_item = compose->aspell_options_menu;
6953                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6954         } else
6955                 compose->aspell_options_menu = parent_item;
6956
6957         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6958
6959         spell_menu = g_slist_reverse(spell_menu);
6960         for (items = spell_menu;
6961              items; items = items->next) {
6962                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6963                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6964                 gtk_widget_show(GTK_WIDGET(menuitem));
6965         }
6966         g_slist_free(spell_menu);
6967
6968         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6969         gtk_widget_show(parent_item);
6970 }
6971
6972 static void compose_dict_changed(void *data)
6973 {
6974         Compose *compose = (Compose *) data;
6975
6976         if(compose->gtkaspell && 
6977            compose->gtkaspell->recheck_when_changing_dict == FALSE)
6978                 return;
6979
6980         gtkaspell_highlight_all(compose->gtkaspell);
6981         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6982 }
6983 #endif
6984
6985 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6986 {
6987         Compose *compose = (Compose *)data;
6988         GdkEventButton event;
6989         
6990         event.button = 3;
6991         event.time = gtk_get_current_event_time();
6992         event.x = 0;
6993         event.y = 0;
6994
6995         return text_clicked(compose->text, &event, compose);
6996 }
6997
6998 static gboolean compose_force_window_origin = TRUE;
6999 static Compose *compose_create(PrefsAccount *account,
7000                                                  FolderItem *folder,
7001                                                  ComposeMode mode,
7002                                                  gboolean batch)
7003 {
7004         Compose   *compose;
7005         GtkWidget *window;
7006         GtkWidget *vbox;
7007         GtkWidget *menubar;
7008         GtkWidget *handlebox;
7009
7010         GtkWidget *notebook;
7011         
7012         GtkWidget *attach_hbox;
7013         GtkWidget *attach_lab1;
7014         GtkWidget *attach_lab2;
7015
7016         GtkWidget *vbox2;
7017
7018         GtkWidget *label;
7019         GtkWidget *subject_hbox;
7020         GtkWidget *subject_frame;
7021         GtkWidget *subject_entry;
7022         GtkWidget *subject;
7023         GtkWidget *paned;
7024
7025         GtkWidget *edit_vbox;
7026         GtkWidget *ruler_hbox;
7027         GtkWidget *ruler;
7028         GtkWidget *scrolledwin;
7029         GtkWidget *text;
7030         GtkTextBuffer *buffer;
7031         GtkClipboard *clipboard;
7032         CLAWS_TIP_DECL();
7033
7034         UndoMain *undostruct;
7035
7036         gchar *titles[N_ATTACH_COLS];
7037         GtkWidget *popupmenu;
7038         GtkWidget *tmpl_menu;
7039         GtkActionGroup *action_group = NULL;
7040
7041 #if USE_ENCHANT
7042         GtkAspell * gtkaspell = NULL;
7043 #endif
7044
7045         static GdkGeometry geometry;
7046
7047         cm_return_val_if_fail(account != NULL, NULL);
7048
7049         debug_print("Creating compose window...\n");
7050         compose = g_new0(Compose, 1);
7051
7052         titles[COL_MIMETYPE] = _("MIME type");
7053         titles[COL_SIZE]     = _("Size");
7054         titles[COL_NAME]     = _("Name");
7055
7056         compose->batch = batch;
7057         compose->account = account;
7058         compose->folder = folder;
7059         
7060         compose->mutex = g_mutex_new();
7061         compose->set_cursor_pos = -1;
7062
7063 #if !(GTK_CHECK_VERSION(2,12,0))
7064         compose->tooltips = tips;
7065 #endif
7066
7067         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7068
7069         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7070         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7071
7072         if (!geometry.max_width) {
7073                 geometry.max_width = gdk_screen_width();
7074                 geometry.max_height = gdk_screen_height();
7075         }
7076
7077         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7078                                       &geometry, GDK_HINT_MAX_SIZE);
7079         if (!geometry.min_width) {
7080                 geometry.min_width = 600;
7081                 geometry.min_height = 440;
7082         }
7083         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7084                                       &geometry, GDK_HINT_MIN_SIZE);
7085
7086 #ifndef GENERIC_UMPC    
7087         if (compose_force_window_origin)
7088                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7089                                  prefs_common.compose_y);
7090 #endif
7091         g_signal_connect(G_OBJECT(window), "delete_event",
7092                          G_CALLBACK(compose_delete_cb), compose);
7093         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7094         gtk_widget_realize(window);
7095
7096         gtkut_widget_set_composer_icon(window);
7097
7098         vbox = gtk_vbox_new(FALSE, 0);
7099         gtk_container_add(GTK_CONTAINER(window), vbox);
7100
7101         compose->ui_manager = gtk_ui_manager_new();
7102         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7103                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7104         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7105                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7106         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7107                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7108         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7109                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7110         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7111                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7112
7113 #ifndef MAEMO
7114         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7115 #else
7116         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7117 #endif
7118
7119         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7120         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7121 #ifdef USE_ENCHANT
7122         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7123 #endif
7124         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7125         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7126         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7127
7128 /* Compose menu */
7129         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7130         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7131         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7132         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7133         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7134         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7135         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7136         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7137         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7138         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7139
7140 /* Edit menu */
7141         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7142         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7143         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7144
7145         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7146         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7147         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7148
7149         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7150         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7151         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7152         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7153
7154         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7155
7156         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7157         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7158         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7159         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7160         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7161         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7162         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7163         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7164         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7165         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7166         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7167         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7168         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7169         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7170         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7171
7172         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7173
7174         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7175         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7176         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7177         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7178         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7179
7180         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7181
7182         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7183
7184 #if USE_ENCHANT
7185 /* Spelling menu */
7186         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7187         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7188         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7189         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7190         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7191         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7192 #endif
7193
7194 /* Options menu */
7195         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7196         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7197         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7198         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7199         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7200
7201         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7202         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7203         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7204         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7205         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7206
7207         
7208         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7209         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7210         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7211         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7212         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7213         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7214         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7215
7216         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7217         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7218         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7219         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7220         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7221
7222         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7223
7224         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7225         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7226         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7227         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7228         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7229
7230         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7231         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)
7232         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)
7233         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7234
7235         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7236
7237         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7238         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)
7239         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)
7240
7241         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7242
7243         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7244         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)
7245         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7246
7247         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7248         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)
7249         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7250
7251         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7252
7253         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7254         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)
7255         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7256         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7257         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7258
7259         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7260         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)
7261         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)
7262         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7263         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7264
7265         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7266         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7267         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7268         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7269         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7270         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7271
7272         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7273         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7274         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)
7275
7276         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7277         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7278         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7279 /* phew. */
7280
7281 /* Tools menu */
7282         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7283         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7284         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7285         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7286         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7287         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7288
7289 /* Help menu */
7290         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7291
7292         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7293         gtk_widget_show_all(menubar);
7294
7295         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7296 #ifndef MAEMO
7297         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7298 #else
7299         hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7300 #endif
7301
7302         if (prefs_common.toolbar_detachable) {
7303                 handlebox = gtk_handle_box_new();
7304         } else {
7305                 handlebox = gtk_hbox_new(FALSE, 0);
7306         }
7307         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7308
7309         gtk_widget_realize(handlebox);
7310 #ifdef MAEMO
7311         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7312                                           (gpointer)compose);
7313 #else
7314         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7315                                           (gpointer)compose);
7316 #endif
7317
7318         vbox2 = gtk_vbox_new(FALSE, 2);
7319         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7320         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7321         
7322         /* Notebook */
7323         notebook = gtk_notebook_new();
7324         gtk_widget_set_size_request(notebook, -1, 130);
7325         gtk_widget_show(notebook);
7326
7327         /* header labels and entries */
7328         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7329                         compose_create_header(compose),
7330                         gtk_label_new_with_mnemonic(_("Hea_der")));
7331         /* attachment list */
7332         attach_hbox = gtk_hbox_new(FALSE, 0);
7333         gtk_widget_show(attach_hbox);
7334         
7335         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7336         gtk_widget_show(attach_lab1);
7337         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7338         
7339         attach_lab2 = gtk_label_new("");
7340         gtk_widget_show(attach_lab2);
7341         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7342         
7343         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7344                         compose_create_attach(compose),
7345                         attach_hbox);
7346         /* Others Tab */
7347         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7348                         compose_create_others(compose),
7349                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7350
7351         /* Subject */
7352         subject_hbox = gtk_hbox_new(FALSE, 0);
7353         gtk_widget_show(subject_hbox);
7354
7355         subject_frame = gtk_frame_new(NULL);
7356         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7357         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7358         gtk_widget_show(subject_frame);
7359
7360         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7361         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7362         gtk_widget_show(subject);
7363
7364         label = gtk_label_new(_("Subject:"));
7365         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7366         gtk_widget_show(label);
7367
7368 #ifdef USE_ENCHANT
7369         subject_entry = claws_spell_entry_new();
7370 #else
7371         subject_entry = gtk_entry_new();
7372 #endif
7373         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7374         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7375                          G_CALLBACK(compose_grab_focus_cb), compose);
7376         gtk_widget_show(subject_entry);
7377         compose->subject_entry = subject_entry;
7378         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7379         
7380         edit_vbox = gtk_vbox_new(FALSE, 0);
7381
7382         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7383
7384         /* ruler */
7385         ruler_hbox = gtk_hbox_new(FALSE, 0);
7386         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7387
7388         ruler = gtk_shruler_new();
7389         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7390         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7391                            BORDER_WIDTH);
7392
7393         /* text widget */
7394         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7395         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7396                                        GTK_POLICY_AUTOMATIC,
7397                                        GTK_POLICY_AUTOMATIC);
7398         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7399                                             GTK_SHADOW_IN);
7400         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7401         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7402
7403         text = gtk_text_view_new();
7404         if (prefs_common.show_compose_margin) {
7405                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7406                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7407         }
7408         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7409         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7410         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7411         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7412         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7413         
7414         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7415
7416         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7417                                G_CALLBACK(compose_edit_size_alloc),
7418                                ruler);
7419         g_signal_connect(G_OBJECT(buffer), "changed",
7420                          G_CALLBACK(compose_changed_cb), compose);
7421         g_signal_connect(G_OBJECT(text), "grab_focus",
7422                          G_CALLBACK(compose_grab_focus_cb), compose);
7423         g_signal_connect(G_OBJECT(buffer), "insert_text",
7424                          G_CALLBACK(text_inserted), compose);
7425         g_signal_connect(G_OBJECT(text), "button_press_event",
7426                          G_CALLBACK(text_clicked), compose);
7427 #ifndef MAEMO
7428         g_signal_connect(G_OBJECT(text), "popup-menu",
7429                          G_CALLBACK(compose_popup_menu), compose);
7430 #else
7431         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7432                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7433         g_signal_connect(G_OBJECT(text), "tap-and-hold",
7434                          G_CALLBACK(compose_popup_menu), compose);
7435 #endif
7436         g_signal_connect(G_OBJECT(subject_entry), "changed",
7437                          G_CALLBACK(compose_changed_cb), compose);
7438
7439         /* drag and drop */
7440         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7441                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7442                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7443         g_signal_connect(G_OBJECT(text), "drag_data_received",
7444                          G_CALLBACK(compose_insert_drag_received_cb),
7445                          compose);
7446         g_signal_connect(G_OBJECT(text), "drag-drop",
7447                          G_CALLBACK(compose_drag_drop),
7448                          compose);
7449         gtk_widget_show_all(vbox);
7450
7451         /* pane between attach clist and text */
7452         paned = gtk_vpaned_new();
7453         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7454 #ifdef MAEMO
7455         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7456                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7457         else
7458                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7459 #endif
7460         gtk_paned_add1(GTK_PANED(paned), notebook);
7461         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7462         gtk_widget_show_all(paned);
7463
7464
7465         if (prefs_common.textfont) {
7466                 PangoFontDescription *font_desc;
7467
7468                 font_desc = pango_font_description_from_string
7469                         (prefs_common.textfont);
7470                 if (font_desc) {
7471                         gtk_widget_modify_font(text, font_desc);
7472                         pango_font_description_free(font_desc);
7473                 }
7474         }
7475
7476         gtk_action_group_add_actions(action_group, compose_popup_entries,
7477                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7478         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7479         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7480         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7481         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7482         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7483         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7484         
7485         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7486
7487         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7488         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7489         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7490
7491         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7492
7493         undostruct = undo_init(text);
7494         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7495                                    compose);
7496
7497         address_completion_start(window);
7498
7499         compose->window        = window;
7500         compose->vbox          = vbox;
7501         compose->menubar       = menubar;
7502         compose->handlebox     = handlebox;
7503
7504         compose->vbox2         = vbox2;
7505
7506         compose->paned = paned;
7507
7508         compose->attach_label  = attach_lab2;
7509
7510         compose->notebook      = notebook;
7511         compose->edit_vbox     = edit_vbox;
7512         compose->ruler_hbox    = ruler_hbox;
7513         compose->ruler         = ruler;
7514         compose->scrolledwin   = scrolledwin;
7515         compose->text          = text;
7516
7517         compose->focused_editable = NULL;
7518
7519         compose->popupmenu    = popupmenu;
7520
7521         compose->tmpl_menu = tmpl_menu;
7522
7523         compose->mode = mode;
7524         compose->rmode = mode;
7525
7526         compose->targetinfo = NULL;
7527         compose->replyinfo  = NULL;
7528         compose->fwdinfo    = NULL;
7529
7530         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7531                                 g_str_equal, (GDestroyNotify) g_free, NULL);
7532         
7533         compose->replyto     = NULL;
7534         compose->cc          = NULL;
7535         compose->bcc         = NULL;
7536         compose->followup_to = NULL;
7537
7538         compose->ml_post     = NULL;
7539
7540         compose->inreplyto   = NULL;
7541         compose->references  = NULL;
7542         compose->msgid       = NULL;
7543         compose->boundary    = NULL;
7544
7545         compose->autowrap       = prefs_common.autowrap;
7546         compose->autoindent     = prefs_common.auto_indent;
7547         compose->use_signing    = FALSE;
7548         compose->use_encryption = FALSE;
7549         compose->privacy_system = NULL;
7550
7551         compose->modified = FALSE;
7552
7553         compose->return_receipt = FALSE;
7554
7555         compose->to_list        = NULL;
7556         compose->newsgroup_list = NULL;
7557
7558         compose->undostruct = undostruct;
7559
7560         compose->sig_str = NULL;
7561
7562         compose->exteditor_file    = NULL;
7563         compose->exteditor_pid     = -1;
7564         compose->exteditor_tag     = -1;
7565         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7566
7567 #if USE_ENCHANT
7568         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7569         if (mode != COMPOSE_REDIRECT) {
7570                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7571                     strcmp(prefs_common.dictionary, "")) {
7572                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
7573                                                   prefs_common.alt_dictionary,
7574                                                   conv_get_locale_charset_str(),
7575                                                   prefs_common.misspelled_col,
7576                                                   prefs_common.check_while_typing,
7577                                                   prefs_common.recheck_when_changing_dict,
7578                                                   prefs_common.use_alternate,
7579                                                   prefs_common.use_both_dicts,
7580                                                   GTK_TEXT_VIEW(text),
7581                                                   GTK_WINDOW(compose->window),
7582                                                   compose_dict_changed,
7583                                                   compose_spell_menu_changed,
7584                                                   compose);
7585                         if (!gtkaspell) {
7586                                 alertpanel_error(_("Spell checker could not "
7587                                                 "be started.\n%s"),
7588                                                 gtkaspell_checkers_strerror());
7589                                 gtkaspell_checkers_reset_error();
7590                         } else {
7591                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7592                         }
7593                 }
7594         }
7595     compose->gtkaspell = gtkaspell;
7596         compose_spell_menu_changed(compose);
7597         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7598 #endif
7599
7600         compose_select_account(compose, account, TRUE);
7601
7602         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7603         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7604
7605         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7606                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7607
7608         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7609                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7610         
7611         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7612                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7613
7614         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7615         if (account->protocol != A_NNTP)
7616                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7617                                 prefs_common_translated_header_name("To:"));
7618         else
7619                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7620                                 prefs_common_translated_header_name("Newsgroups:"));
7621
7622         addressbook_set_target_compose(compose);
7623         
7624         if (mode != COMPOSE_REDIRECT)
7625                 compose_set_template_menu(compose);
7626         else {
7627                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7628         }
7629
7630         compose_list = g_list_append(compose_list, compose);
7631
7632         if (!prefs_common.show_ruler)
7633                 gtk_widget_hide(ruler_hbox);
7634                 
7635         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7636
7637         /* Priority */
7638         compose->priority = PRIORITY_NORMAL;
7639         compose_update_priority_menu_item(compose);
7640
7641         compose_set_out_encoding(compose);
7642         
7643         /* Actions menu */
7644         compose_update_actions_menu(compose);
7645
7646         /* Privacy Systems menu */
7647         compose_update_privacy_systems_menu(compose);
7648
7649         activate_privacy_system(compose, account, TRUE);
7650         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7651         if (batch) {
7652                 gtk_widget_realize(window);
7653         } else {
7654                 gtk_widget_show(window);
7655 #ifdef MAEMO
7656                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7657                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7658 #endif
7659         }
7660         
7661         return compose;
7662 }
7663
7664 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7665 {
7666         GList *accounts;
7667         GtkWidget *hbox;
7668         GtkWidget *optmenu;
7669         GtkWidget *optmenubox;
7670         GtkListStore *menu;
7671         GtkTreeIter iter;
7672         GtkWidget *from_name = NULL;
7673 #if !(GTK_CHECK_VERSION(2,12,0))
7674         GtkTooltips *tips = compose->tooltips;
7675 #endif
7676
7677         gint num = 0, def_menu = 0;
7678         
7679         accounts = account_get_list();
7680         cm_return_val_if_fail(accounts != NULL, NULL);
7681
7682         optmenubox = gtk_event_box_new();
7683         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7684         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7685
7686         hbox = gtk_hbox_new(FALSE, 6);
7687         from_name = gtk_entry_new();
7688         
7689         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7690                          G_CALLBACK(compose_grab_focus_cb), compose);
7691
7692         for (; accounts != NULL; accounts = accounts->next, num++) {
7693                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7694                 gchar *name, *from = NULL;
7695
7696                 if (ac == compose->account) def_menu = num;
7697
7698                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7699                                        ac->account_name);
7700                 
7701                 if (ac == compose->account) {
7702                         if (ac->name && *ac->name) {
7703                                 gchar *buf;
7704                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7705                                 from = g_strdup_printf("%s <%s>",
7706                                                        buf, ac->address);
7707                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7708                         } else {
7709                                 from = g_strdup_printf("%s",
7710                                                        ac->address);
7711                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7712                         }
7713                 }
7714                 COMBOBOX_ADD(menu, name, ac->account_id);
7715                 g_free(name);
7716                 g_free(from);
7717         }
7718
7719         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7720
7721         g_signal_connect(G_OBJECT(optmenu), "changed",
7722                         G_CALLBACK(account_activated),
7723                         compose);
7724         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7725                          G_CALLBACK(compose_entry_popup_extend),
7726                          NULL);
7727
7728         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7729         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7730         
7731         CLAWS_SET_TIP(optmenubox,
7732                 _("Account to use for this email"));
7733         CLAWS_SET_TIP(from_name,
7734                 _("Sender address to be used"));
7735
7736         compose->account_combo = optmenu;
7737         compose->from_name = from_name;
7738         
7739         return hbox;
7740 }
7741
7742 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7743 {
7744         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7745         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7746         Compose *compose = (Compose *) data;
7747         if (active) {
7748                 compose->priority = value;
7749         }
7750 }
7751
7752 static void compose_reply_change_mode(Compose *compose,
7753                                     ComposeMode action)
7754 {
7755         gboolean was_modified = compose->modified;
7756
7757         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7758         
7759         cm_return_if_fail(compose->replyinfo != NULL);
7760         
7761         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7762                 ml = TRUE;
7763         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7764                 followup = TRUE;
7765         if (action == COMPOSE_REPLY_TO_ALL)
7766                 all = TRUE;
7767         if (action == COMPOSE_REPLY_TO_SENDER)
7768                 sender = TRUE;
7769         if (action == COMPOSE_REPLY_TO_LIST)
7770                 ml = TRUE;
7771
7772         compose_remove_header_entries(compose);
7773         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7774         if (compose->account->set_autocc && compose->account->auto_cc)
7775                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7776
7777         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7778                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7779         
7780         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7781                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7782         compose_show_first_last_header(compose, TRUE);
7783         compose->modified = was_modified;
7784         compose_set_title(compose);
7785 }
7786
7787 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7788 {
7789         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7790         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7791         Compose *compose = (Compose *) data;
7792         
7793         if (active)
7794                 compose_reply_change_mode(compose, value);
7795 }
7796
7797 static void compose_update_priority_menu_item(Compose * compose)
7798 {
7799         GtkWidget *menuitem = NULL;
7800         switch (compose->priority) {
7801                 case PRIORITY_HIGHEST:
7802                         menuitem = gtk_ui_manager_get_widget
7803                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7804                         break;
7805                 case PRIORITY_HIGH:
7806                         menuitem = gtk_ui_manager_get_widget
7807                                 (compose->ui_manager, "/Menu/Options/Priority/High");
7808                         break;
7809                 case PRIORITY_NORMAL:
7810                         menuitem = gtk_ui_manager_get_widget
7811                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7812                         break;
7813                 case PRIORITY_LOW:
7814                         menuitem = gtk_ui_manager_get_widget
7815                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
7816                         break;
7817                 case PRIORITY_LOWEST:
7818                         menuitem = gtk_ui_manager_get_widget
7819                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7820                         break;
7821         }
7822         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7823 }       
7824
7825 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7826 {
7827         Compose *compose = (Compose *) data;
7828         gchar *systemid;
7829         gboolean can_sign = FALSE, can_encrypt = FALSE;
7830
7831         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7832
7833         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7834                 return;
7835
7836         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7837         g_free(compose->privacy_system);
7838         compose->privacy_system = NULL;
7839         if (systemid != NULL) {
7840                 compose->privacy_system = g_strdup(systemid);
7841
7842                 can_sign = privacy_system_can_sign(systemid);
7843                 can_encrypt = privacy_system_can_encrypt(systemid);
7844         }
7845
7846         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7847
7848         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7849         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7850 }
7851
7852 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7853 {
7854         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7855         GtkWidget *menuitem = NULL;
7856         GList *amenu;
7857         gboolean can_sign = FALSE, can_encrypt = FALSE;
7858         gboolean found = FALSE;
7859
7860         if (compose->privacy_system != NULL) {
7861                 gchar *systemid;
7862                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7863                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7864                 cm_return_if_fail(menuitem != NULL);
7865
7866                 amenu = GTK_MENU_SHELL(menuitem)->children;
7867                 menuitem = NULL;
7868                 while (amenu != NULL) {
7869                         GList *alist = amenu->next;
7870
7871                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7872                         if (systemid != NULL) {
7873                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
7874                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7875                                         menuitem = GTK_WIDGET(amenu->data);
7876
7877                                         can_sign = privacy_system_can_sign(systemid);
7878                                         can_encrypt = privacy_system_can_encrypt(systemid);
7879                                         found = TRUE;
7880                                         break;
7881                                 } 
7882                         } else if (strlen(compose->privacy_system) == 0 && 
7883                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7884                                         menuitem = GTK_WIDGET(amenu->data);
7885
7886                                         can_sign = FALSE;
7887                                         can_encrypt = FALSE;
7888                                         found = TRUE;
7889                                         break;
7890                         }
7891
7892                         amenu = alist;
7893                 }
7894                 if (menuitem != NULL)
7895                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7896                 
7897                 if (warn && !found && strlen(compose->privacy_system)) {
7898                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7899                                   "will not be able to sign or encrypt this message."),
7900                                   compose->privacy_system);
7901                 }
7902         } 
7903
7904         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7905         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7906 }       
7907  
7908 static void compose_set_out_encoding(Compose *compose)
7909 {
7910         CharSet out_encoding;
7911         const gchar *branch = NULL;
7912         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7913
7914         switch(out_encoding) {
7915                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7916                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7917                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7918                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7919                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7920                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7921                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7922                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7923                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7924                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7925                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7926                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7927                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7928                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7929                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7930                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7931                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7932                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7933                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7934                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7935                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7936                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7937                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7938                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7939                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7940                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7941                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7942                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7943                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7944                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7945                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7946                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7947                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7948         }
7949         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7950 }
7951
7952 static void compose_set_template_menu(Compose *compose)
7953 {
7954         GSList *tmpl_list, *cur;
7955         GtkWidget *menu;
7956         GtkWidget *item;
7957
7958         tmpl_list = template_get_config();
7959
7960         menu = gtk_menu_new();
7961
7962         gtk_menu_set_accel_group (GTK_MENU (menu), 
7963                 gtk_ui_manager_get_accel_group(compose->ui_manager));
7964         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7965                 Template *tmpl = (Template *)cur->data;
7966                 gchar *accel_path = NULL;
7967                 item = gtk_menu_item_new_with_label(tmpl->name);
7968                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7969                 g_signal_connect(G_OBJECT(item), "activate",
7970                                  G_CALLBACK(compose_template_activate_cb),
7971                                  compose);
7972                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7973                 gtk_widget_show(item);
7974                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7975                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7976                 g_free(accel_path);
7977         }
7978
7979         gtk_widget_show(menu);
7980         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7981 }
7982
7983 void compose_update_actions_menu(Compose *compose)
7984 {
7985         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7986 }
7987
7988 static void compose_update_privacy_systems_menu(Compose *compose)
7989 {
7990         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7991         GSList *systems, *cur;
7992         GtkWidget *widget;
7993         GtkWidget *system_none;
7994         GSList *group;
7995         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7996         GtkWidget *privacy_menu = gtk_menu_new();
7997
7998         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7999         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8000
8001         g_signal_connect(G_OBJECT(system_none), "activate",
8002                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8003
8004         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8005         gtk_widget_show(system_none);
8006
8007         systems = privacy_get_system_ids();
8008         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8009                 gchar *systemid = cur->data;
8010
8011                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8012                 widget = gtk_radio_menu_item_new_with_label(group,
8013                         privacy_system_get_name(systemid));
8014                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8015                                        g_strdup(systemid), g_free);
8016                 g_signal_connect(G_OBJECT(widget), "activate",
8017                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8018
8019                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8020                 gtk_widget_show(widget);
8021                 g_free(systemid);
8022         }
8023         g_slist_free(systems);
8024         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8025         gtk_widget_show_all(privacy_menu);
8026         gtk_widget_show_all(privacy_menuitem);
8027 }
8028
8029 void compose_reflect_prefs_all(void)
8030 {
8031         GList *cur;
8032         Compose *compose;
8033
8034         for (cur = compose_list; cur != NULL; cur = cur->next) {
8035                 compose = (Compose *)cur->data;
8036                 compose_set_template_menu(compose);
8037         }
8038 }
8039
8040 void compose_reflect_prefs_pixmap_theme(void)
8041 {
8042         GList *cur;
8043         Compose *compose;
8044
8045         for (cur = compose_list; cur != NULL; cur = cur->next) {
8046                 compose = (Compose *)cur->data;
8047                 toolbar_update(TOOLBAR_COMPOSE, compose);
8048         }
8049 }
8050
8051 static const gchar *compose_quote_char_from_context(Compose *compose)
8052 {
8053         const gchar *qmark = NULL;
8054
8055         cm_return_val_if_fail(compose != NULL, NULL);
8056
8057         switch (compose->mode) {
8058                 /* use forward-specific quote char */
8059                 case COMPOSE_FORWARD:
8060                 case COMPOSE_FORWARD_AS_ATTACH:
8061                 case COMPOSE_FORWARD_INLINE:
8062                         if (compose->folder && compose->folder->prefs &&
8063                                         compose->folder->prefs->forward_with_format)
8064                                 qmark = compose->folder->prefs->forward_quotemark;
8065                         else if (compose->account->forward_with_format)
8066                                 qmark = compose->account->forward_quotemark;
8067                         else
8068                                 qmark = prefs_common.fw_quotemark;
8069                         break;
8070
8071                 /* use reply-specific quote char in all other modes */
8072                 default:
8073                         if (compose->folder && compose->folder->prefs &&
8074                                         compose->folder->prefs->reply_with_format)
8075                                 qmark = compose->folder->prefs->reply_quotemark;
8076                         else if (compose->account->reply_with_format)
8077                                 qmark = compose->account->reply_quotemark;
8078                         else
8079                                 qmark = prefs_common.quotemark;
8080                         break;
8081         }
8082
8083         if (qmark == NULL || *qmark == '\0')
8084                 qmark = "> ";
8085
8086         return qmark;
8087 }
8088
8089 static void compose_template_apply(Compose *compose, Template *tmpl,
8090                                    gboolean replace)
8091 {
8092         GtkTextView *text;
8093         GtkTextBuffer *buffer;
8094         GtkTextMark *mark;
8095         GtkTextIter iter;
8096         const gchar *qmark;
8097         gchar *parsed_str = NULL;
8098         gint cursor_pos = 0;
8099         const gchar *err_msg = _("The body of the template has an error at line %d.");
8100         if (!tmpl) return;
8101
8102         /* process the body */
8103
8104         text = GTK_TEXT_VIEW(compose->text);
8105         buffer = gtk_text_view_get_buffer(text);
8106
8107         if (tmpl->value) {
8108                 qmark = compose_quote_char_from_context(compose);
8109
8110                 if (compose->replyinfo != NULL) {
8111
8112                         if (replace)
8113                                 gtk_text_buffer_set_text(buffer, "", -1);
8114                         mark = gtk_text_buffer_get_insert(buffer);
8115                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8116
8117                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8118                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8119
8120                 } else if (compose->fwdinfo != NULL) {
8121
8122                         if (replace)
8123                                 gtk_text_buffer_set_text(buffer, "", -1);
8124                         mark = gtk_text_buffer_get_insert(buffer);
8125                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8126
8127                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8128                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8129
8130                 } else {
8131                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8132
8133                         GtkTextIter start, end;
8134                         gchar *tmp = NULL;
8135
8136                         gtk_text_buffer_get_start_iter(buffer, &start);
8137                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8138                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8139
8140                         /* clear the buffer now */
8141                         if (replace)
8142                                 gtk_text_buffer_set_text(buffer, "", -1);
8143
8144                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8145                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8146                         procmsg_msginfo_free( dummyinfo );
8147
8148                         g_free( tmp );
8149                 } 
8150         } else {
8151                 if (replace)
8152                         gtk_text_buffer_set_text(buffer, "", -1);
8153                 mark = gtk_text_buffer_get_insert(buffer);
8154                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8155         }       
8156
8157         if (replace && parsed_str && compose->account->auto_sig)
8158                 compose_insert_sig(compose, FALSE);
8159
8160         if (replace && parsed_str) {
8161                 gtk_text_buffer_get_start_iter(buffer, &iter);
8162                 gtk_text_buffer_place_cursor(buffer, &iter);
8163         }
8164         
8165         if (parsed_str) {
8166                 cursor_pos = quote_fmt_get_cursor_pos();
8167                 compose->set_cursor_pos = cursor_pos;
8168                 if (cursor_pos == -1)
8169                         cursor_pos = 0;
8170                 gtk_text_buffer_get_start_iter(buffer, &iter);
8171                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8172                 gtk_text_buffer_place_cursor(buffer, &iter);
8173         }
8174
8175         /* process the other fields */
8176
8177         compose_template_apply_fields(compose, tmpl);
8178         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8179         quote_fmt_reset_vartable();
8180         compose_changed_cb(NULL, compose);
8181
8182 #ifdef USE_ENCHANT
8183         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8184                 gtkaspell_highlight_all(compose->gtkaspell);
8185 #endif
8186 }
8187
8188 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8189 {
8190         MsgInfo* dummyinfo = NULL;
8191         MsgInfo *msginfo = NULL;
8192         gchar *buf = NULL;
8193
8194         if (compose->replyinfo != NULL)
8195                 msginfo = compose->replyinfo;
8196         else if (compose->fwdinfo != NULL)
8197                 msginfo = compose->fwdinfo;
8198         else {
8199                 dummyinfo = compose_msginfo_new_from_compose(compose);
8200                 msginfo = dummyinfo;
8201         }
8202
8203         if (tmpl->from && *tmpl->from != '\0') {
8204 #ifdef USE_ENCHANT
8205                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8206                                 compose->gtkaspell);
8207 #else
8208                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8209 #endif
8210                 quote_fmt_scan_string(tmpl->from);
8211                 quote_fmt_parse();
8212
8213                 buf = quote_fmt_get_buffer();
8214                 if (buf == NULL) {
8215                         alertpanel_error(_("Template From format error."));
8216                 } else {
8217                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8218                 }
8219         }
8220
8221         if (tmpl->to && *tmpl->to != '\0') {
8222 #ifdef USE_ENCHANT
8223                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8224                                 compose->gtkaspell);
8225 #else
8226                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8227 #endif
8228                 quote_fmt_scan_string(tmpl->to);
8229                 quote_fmt_parse();
8230
8231                 buf = quote_fmt_get_buffer();
8232                 if (buf == NULL) {
8233                         alertpanel_error(_("Template To format error."));
8234                 } else {
8235                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8236                 }
8237         }
8238
8239         if (tmpl->cc && *tmpl->cc != '\0') {
8240 #ifdef USE_ENCHANT
8241                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8242                                 compose->gtkaspell);
8243 #else
8244                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8245 #endif
8246                 quote_fmt_scan_string(tmpl->cc);
8247                 quote_fmt_parse();
8248
8249                 buf = quote_fmt_get_buffer();
8250                 if (buf == NULL) {
8251                         alertpanel_error(_("Template Cc format error."));
8252                 } else {
8253                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8254                 }
8255         }
8256
8257         if (tmpl->bcc && *tmpl->bcc != '\0') {
8258 #ifdef USE_ENCHANT
8259                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8260                                 compose->gtkaspell);
8261 #else
8262                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8263 #endif
8264                 quote_fmt_scan_string(tmpl->bcc);
8265                 quote_fmt_parse();
8266
8267                 buf = quote_fmt_get_buffer();
8268                 if (buf == NULL) {
8269                         alertpanel_error(_("Template Bcc format error."));
8270                 } else {
8271                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8272                 }
8273         }
8274
8275         /* process the subject */
8276         if (tmpl->subject && *tmpl->subject != '\0') {
8277 #ifdef USE_ENCHANT
8278                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8279                                 compose->gtkaspell);
8280 #else
8281                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8282 #endif
8283                 quote_fmt_scan_string(tmpl->subject);
8284                 quote_fmt_parse();
8285
8286                 buf = quote_fmt_get_buffer();
8287                 if (buf == NULL) {
8288                         alertpanel_error(_("Template subject format error."));
8289                 } else {
8290                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8291                 }
8292         }
8293
8294         procmsg_msginfo_free( dummyinfo );
8295 }
8296
8297 static void compose_destroy(Compose *compose)
8298 {
8299         GtkTextBuffer *buffer;
8300         GtkClipboard *clipboard;
8301
8302         compose_list = g_list_remove(compose_list, compose);
8303
8304         if (compose->updating) {
8305                 debug_print("danger, not destroying anything now\n");
8306                 compose->deferred_destroy = TRUE;
8307                 return;
8308         }
8309         /* NOTE: address_completion_end() does nothing with the window
8310          * however this may change. */
8311         address_completion_end(compose->window);
8312
8313         slist_free_strings(compose->to_list);
8314         g_slist_free(compose->to_list);
8315         slist_free_strings(compose->newsgroup_list);
8316         g_slist_free(compose->newsgroup_list);
8317         slist_free_strings(compose->header_list);
8318         g_slist_free(compose->header_list);
8319
8320         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8321
8322         g_hash_table_destroy(compose->email_hashtable);
8323
8324         procmsg_msginfo_free(compose->targetinfo);
8325         procmsg_msginfo_free(compose->replyinfo);
8326         procmsg_msginfo_free(compose->fwdinfo);
8327
8328         g_free(compose->replyto);
8329         g_free(compose->cc);
8330         g_free(compose->bcc);
8331         g_free(compose->newsgroups);
8332         g_free(compose->followup_to);
8333
8334         g_free(compose->ml_post);
8335
8336         g_free(compose->inreplyto);
8337         g_free(compose->references);
8338         g_free(compose->msgid);
8339         g_free(compose->boundary);
8340
8341         g_free(compose->redirect_filename);
8342         if (compose->undostruct)
8343                 undo_destroy(compose->undostruct);
8344
8345         g_free(compose->sig_str);
8346
8347         g_free(compose->exteditor_file);
8348
8349         g_free(compose->orig_charset);
8350
8351         g_free(compose->privacy_system);
8352
8353         if (addressbook_get_target_compose() == compose)
8354                 addressbook_set_target_compose(NULL);
8355
8356 #if USE_ENCHANT
8357         if (compose->gtkaspell) {
8358                 gtkaspell_delete(compose->gtkaspell);
8359                 compose->gtkaspell = NULL;
8360         }
8361 #endif
8362
8363         if (!compose->batch) {
8364                 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8365                 prefs_common.compose_height = compose->window->allocation.height;
8366         }
8367
8368         if (!gtk_widget_get_parent(compose->paned))
8369                 gtk_widget_destroy(compose->paned);
8370         gtk_widget_destroy(compose->popupmenu);
8371
8372         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8373         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8374         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8375
8376         gtk_widget_destroy(compose->window);
8377         toolbar_destroy(compose->toolbar);
8378         g_free(compose->toolbar);
8379         g_mutex_free(compose->mutex);
8380         g_free(compose);
8381 }
8382
8383 static void compose_attach_info_free(AttachInfo *ainfo)
8384 {
8385         g_free(ainfo->file);
8386         g_free(ainfo->content_type);
8387         g_free(ainfo->name);
8388         g_free(ainfo);
8389 }
8390
8391 static void compose_attach_update_label(Compose *compose)
8392 {
8393         GtkTreeIter iter;
8394         gint i = 1;
8395         gchar *text;
8396         GtkTreeModel *model;
8397         
8398         if(compose == NULL)
8399                 return;
8400                 
8401         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8402         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8403                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8404                 return;
8405         }
8406         
8407         while(gtk_tree_model_iter_next(model, &iter))
8408                 i++;
8409         
8410         text = g_strdup_printf("(%d)", i);
8411         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8412         g_free(text);
8413 }
8414
8415 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8416 {
8417         Compose *compose = (Compose *)data;
8418         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8419         GtkTreeSelection *selection;
8420         GList *sel, *cur;
8421         GtkTreeModel *model;
8422
8423         selection = gtk_tree_view_get_selection(tree_view);
8424         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8425
8426         if (!sel) 
8427                 return;
8428
8429         for (cur = sel; cur != NULL; cur = cur->next) {
8430                 GtkTreePath *path = cur->data;
8431                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8432                                                 (model, cur->data);
8433                 cur->data = ref;
8434                 gtk_tree_path_free(path);
8435         }
8436
8437         for (cur = sel; cur != NULL; cur = cur->next) {
8438                 GtkTreeRowReference *ref = cur->data;
8439                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8440                 GtkTreeIter iter;
8441
8442                 if (gtk_tree_model_get_iter(model, &iter, path))
8443                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8444                 
8445                 gtk_tree_path_free(path);
8446                 gtk_tree_row_reference_free(ref);
8447         }
8448
8449         g_list_free(sel);
8450         compose_attach_update_label(compose);
8451 }
8452
8453 static struct _AttachProperty
8454 {
8455         GtkWidget *window;
8456         GtkWidget *mimetype_entry;
8457         GtkWidget *encoding_optmenu;
8458         GtkWidget *path_entry;
8459         GtkWidget *filename_entry;
8460         GtkWidget *ok_btn;
8461         GtkWidget *cancel_btn;
8462 } attach_prop;
8463
8464 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8465 {       
8466         gtk_tree_path_free((GtkTreePath *)ptr);
8467 }
8468
8469 static void compose_attach_property(GtkAction *action, gpointer data)
8470 {
8471         Compose *compose = (Compose *)data;
8472         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8473         AttachInfo *ainfo;
8474         GtkComboBox *optmenu;
8475         GtkTreeSelection *selection;
8476         GList *sel;
8477         GtkTreeModel *model;
8478         GtkTreeIter iter;
8479         GtkTreePath *path;
8480         static gboolean cancelled;
8481
8482         /* only if one selected */
8483         selection = gtk_tree_view_get_selection(tree_view);
8484         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8485                 return;
8486
8487         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8488         if (!sel)
8489                 return;
8490
8491         path = (GtkTreePath *) sel->data;
8492         gtk_tree_model_get_iter(model, &iter, path);
8493         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8494         
8495         if (!ainfo) {
8496                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8497                 g_list_free(sel);
8498                 return;
8499         }               
8500         g_list_free(sel);
8501
8502         if (!attach_prop.window)
8503                 compose_attach_property_create(&cancelled);
8504         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8505         gtk_widget_grab_focus(attach_prop.ok_btn);
8506         gtk_widget_show(attach_prop.window);
8507         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8508
8509         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8510         if (ainfo->encoding == ENC_UNKNOWN)
8511                 combobox_select_by_data(optmenu, ENC_BASE64);
8512         else
8513                 combobox_select_by_data(optmenu, ainfo->encoding);
8514
8515         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8516                            ainfo->content_type ? ainfo->content_type : "");
8517         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8518                            ainfo->file ? ainfo->file : "");
8519         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8520                            ainfo->name ? ainfo->name : "");
8521
8522         for (;;) {
8523                 const gchar *entry_text;
8524                 gchar *text;
8525                 gchar *cnttype = NULL;
8526                 gchar *file = NULL;
8527                 off_t size = 0;
8528
8529                 cancelled = FALSE;
8530                 gtk_main();
8531
8532                 gtk_widget_hide(attach_prop.window);
8533                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8534                 
8535                 if (cancelled) 
8536                         break;
8537
8538                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8539                 if (*entry_text != '\0') {
8540                         gchar *p;
8541
8542                         text = g_strstrip(g_strdup(entry_text));
8543                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8544                                 cnttype = g_strdup(text);
8545                                 g_free(text);
8546                         } else {
8547                                 alertpanel_error(_("Invalid MIME type."));
8548                                 g_free(text);
8549                                 continue;
8550                         }
8551                 }
8552
8553                 ainfo->encoding = combobox_get_active_data(optmenu);
8554
8555                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8556                 if (*entry_text != '\0') {
8557                         if (is_file_exist(entry_text) &&
8558                             (size = get_file_size(entry_text)) > 0)
8559                                 file = g_strdup(entry_text);
8560                         else {
8561                                 alertpanel_error
8562                                         (_("File doesn't exist or is empty."));
8563                                 g_free(cnttype);
8564                                 continue;
8565                         }
8566                 }
8567
8568                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8569                 if (*entry_text != '\0') {
8570                         g_free(ainfo->name);
8571                         ainfo->name = g_strdup(entry_text);
8572                 }
8573
8574                 if (cnttype) {
8575                         g_free(ainfo->content_type);
8576                         ainfo->content_type = cnttype;
8577                 }
8578                 if (file) {
8579                         g_free(ainfo->file);
8580                         ainfo->file = file;
8581                 }
8582                 if (size)
8583                         ainfo->size = (goffset)size;
8584
8585                 /* update tree store */
8586                 text = to_human_readable(ainfo->size);
8587                 gtk_tree_model_get_iter(model, &iter, path);
8588                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8589                                    COL_MIMETYPE, ainfo->content_type,
8590                                    COL_SIZE, text,
8591                                    COL_NAME, ainfo->name,
8592                                    -1);
8593                 
8594                 break;
8595         }
8596
8597         gtk_tree_path_free(path);
8598 }
8599
8600 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8601 { \
8602         label = gtk_label_new(str); \
8603         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8604                          GTK_FILL, 0, 0, 0); \
8605         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8606  \
8607         entry = gtk_entry_new(); \
8608         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8609                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8610 }
8611
8612 static void compose_attach_property_create(gboolean *cancelled)
8613 {
8614         GtkWidget *window;
8615         GtkWidget *vbox;
8616         GtkWidget *table;
8617         GtkWidget *label;
8618         GtkWidget *mimetype_entry;
8619         GtkWidget *hbox;
8620         GtkWidget *optmenu;
8621         GtkListStore *optmenu_menu;
8622         GtkWidget *path_entry;
8623         GtkWidget *filename_entry;
8624         GtkWidget *hbbox;
8625         GtkWidget *ok_btn;
8626         GtkWidget *cancel_btn;
8627         GList     *mime_type_list, *strlist;
8628         GtkTreeIter iter;
8629
8630         debug_print("Creating attach_property window...\n");
8631
8632         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8633         gtk_widget_set_size_request(window, 480, -1);
8634         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8635         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8636         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8637         g_signal_connect(G_OBJECT(window), "delete_event",
8638                          G_CALLBACK(attach_property_delete_event),
8639                          cancelled);
8640         g_signal_connect(G_OBJECT(window), "key_press_event",
8641                          G_CALLBACK(attach_property_key_pressed),
8642                          cancelled);
8643
8644         vbox = gtk_vbox_new(FALSE, 8);
8645         gtk_container_add(GTK_CONTAINER(window), vbox);
8646
8647         table = gtk_table_new(4, 2, FALSE);
8648         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8649         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8650         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8651
8652         label = gtk_label_new(_("MIME type")); 
8653         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8654                          GTK_FILL, 0, 0, 0); 
8655         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8656         mimetype_entry = gtk_combo_box_entry_new_text(); 
8657         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8658                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8659                          
8660         /* stuff with list */
8661         mime_type_list = procmime_get_mime_type_list();
8662         strlist = NULL;
8663         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8664                 MimeType *type = (MimeType *) mime_type_list->data;
8665                 gchar *tmp;
8666
8667                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8668
8669                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8670                         g_free(tmp);
8671                 else
8672                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8673                                         (GCompareFunc)strcmp2);
8674         }
8675
8676         for (mime_type_list = strlist; mime_type_list != NULL; 
8677                 mime_type_list = mime_type_list->next) {
8678                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8679                 g_free(mime_type_list->data);
8680         }
8681         g_list_free(strlist);
8682         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8683         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8684
8685         label = gtk_label_new(_("Encoding"));
8686         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8687                          GTK_FILL, 0, 0, 0);
8688         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8689
8690         hbox = gtk_hbox_new(FALSE, 0);
8691         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8692                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8693
8694         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8695         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8696
8697         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8698         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8699         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8700         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8701         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8702
8703         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8704
8705         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8706         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8707
8708         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8709                                       &ok_btn, GTK_STOCK_OK,
8710                                       NULL, NULL);
8711         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8712         gtk_widget_grab_default(ok_btn);
8713
8714         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8715                          G_CALLBACK(attach_property_ok),
8716                          cancelled);
8717         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8718                          G_CALLBACK(attach_property_cancel),
8719                          cancelled);
8720
8721         gtk_widget_show_all(vbox);
8722
8723         attach_prop.window           = window;
8724         attach_prop.mimetype_entry   = mimetype_entry;
8725         attach_prop.encoding_optmenu = optmenu;
8726         attach_prop.path_entry       = path_entry;
8727         attach_prop.filename_entry   = filename_entry;
8728         attach_prop.ok_btn           = ok_btn;
8729         attach_prop.cancel_btn       = cancel_btn;
8730 }
8731
8732 #undef SET_LABEL_AND_ENTRY
8733
8734 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8735 {
8736         *cancelled = FALSE;
8737         gtk_main_quit();
8738 }
8739
8740 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8741 {
8742         *cancelled = TRUE;
8743         gtk_main_quit();
8744 }
8745
8746 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8747                                          gboolean *cancelled)
8748 {
8749         *cancelled = TRUE;
8750         gtk_main_quit();
8751
8752         return TRUE;
8753 }
8754
8755 static gboolean attach_property_key_pressed(GtkWidget *widget,
8756                                             GdkEventKey *event,
8757                                             gboolean *cancelled)
8758 {
8759         if (event && event->keyval == GDK_Escape) {
8760                 *cancelled = TRUE;
8761                 gtk_main_quit();
8762         }
8763         if (event && event->keyval == GDK_Return) {
8764                 *cancelled = FALSE;
8765                 gtk_main_quit();
8766                 return TRUE;
8767         }
8768         return FALSE;
8769 }
8770
8771 static void compose_exec_ext_editor(Compose *compose)
8772 {
8773 #ifdef G_OS_UNIX
8774         gchar *tmp;
8775         pid_t pid;
8776         gint pipe_fds[2];
8777
8778         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8779                               G_DIR_SEPARATOR, compose);
8780
8781         if (pipe(pipe_fds) < 0) {
8782                 perror("pipe");
8783                 g_free(tmp);
8784                 return;
8785         }
8786
8787         if ((pid = fork()) < 0) {
8788                 perror("fork");
8789                 g_free(tmp);
8790                 return;
8791         }
8792
8793         if (pid != 0) {
8794                 /* close the write side of the pipe */
8795                 close(pipe_fds[1]);
8796
8797                 compose->exteditor_file    = g_strdup(tmp);
8798                 compose->exteditor_pid     = pid;
8799
8800                 compose_set_ext_editor_sensitive(compose, FALSE);
8801
8802 #ifndef G_OS_WIN32
8803                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8804 #else
8805                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8806 #endif
8807                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8808                                                         G_IO_IN,
8809                                                         compose_input_cb,
8810                                                         compose);
8811         } else {        /* process-monitoring process */
8812                 pid_t pid_ed;
8813
8814                 if (setpgid(0, 0))
8815                         perror("setpgid");
8816
8817                 /* close the read side of the pipe */
8818                 close(pipe_fds[0]);
8819
8820                 if (compose_write_body_to_file(compose, tmp) < 0) {
8821                         fd_write_all(pipe_fds[1], "2\n", 2);
8822                         _exit(1);
8823                 }
8824
8825                 pid_ed = compose_exec_ext_editor_real(tmp);
8826                 if (pid_ed < 0) {
8827                         fd_write_all(pipe_fds[1], "1\n", 2);
8828                         _exit(1);
8829                 }
8830
8831                 /* wait until editor is terminated */
8832                 waitpid(pid_ed, NULL, 0);
8833
8834                 fd_write_all(pipe_fds[1], "0\n", 2);
8835
8836                 close(pipe_fds[1]);
8837                 _exit(0);
8838         }
8839
8840         g_free(tmp);
8841 #endif /* G_OS_UNIX */
8842 }
8843
8844 #ifdef G_OS_UNIX
8845 static gint compose_exec_ext_editor_real(const gchar *file)
8846 {
8847         gchar buf[1024];
8848         gchar *p;
8849         gchar **cmdline;
8850         pid_t pid;
8851
8852         cm_return_val_if_fail(file != NULL, -1);
8853
8854         if ((pid = fork()) < 0) {
8855                 perror("fork");
8856                 return -1;
8857         }
8858
8859         if (pid != 0) return pid;
8860
8861         /* grandchild process */
8862
8863         if (setpgid(0, getppid()))
8864                 perror("setpgid");
8865
8866         if (prefs_common_get_ext_editor_cmd() &&
8867             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8868             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8869                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8870         } else {
8871                 if (prefs_common_get_ext_editor_cmd())
8872                         g_warning("External editor command-line is invalid: '%s'\n",
8873                                   prefs_common_get_ext_editor_cmd());
8874                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8875         }
8876
8877         cmdline = strsplit_with_quote(buf, " ", 1024);
8878         execvp(cmdline[0], cmdline);
8879
8880         perror("execvp");
8881         g_strfreev(cmdline);
8882
8883         _exit(1);
8884 }
8885
8886 static gboolean compose_ext_editor_kill(Compose *compose)
8887 {
8888         pid_t pgid = compose->exteditor_pid * -1;
8889         gint ret;
8890
8891         ret = kill(pgid, 0);
8892
8893         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8894                 AlertValue val;
8895                 gchar *msg;
8896
8897                 msg = g_strdup_printf
8898                         (_("The external editor is still working.\n"
8899                            "Force terminating the process?\n"
8900                            "process group id: %d"), -pgid);
8901                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8902                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8903                         
8904                 g_free(msg);
8905
8906                 if (val == G_ALERTALTERNATE) {
8907                         g_source_remove(compose->exteditor_tag);
8908                         g_io_channel_shutdown(compose->exteditor_ch,
8909                                               FALSE, NULL);
8910                         g_io_channel_unref(compose->exteditor_ch);
8911
8912                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8913                         waitpid(compose->exteditor_pid, NULL, 0);
8914
8915                         g_warning("Terminated process group id: %d", -pgid);
8916                         g_warning("Temporary file: %s",
8917                                   compose->exteditor_file);
8918
8919                         compose_set_ext_editor_sensitive(compose, TRUE);
8920
8921                         g_free(compose->exteditor_file);
8922                         compose->exteditor_file    = NULL;
8923                         compose->exteditor_pid     = -1;
8924                         compose->exteditor_ch      = NULL;
8925                         compose->exteditor_tag     = -1;
8926                 } else
8927                         return FALSE;
8928         }
8929
8930         return TRUE;
8931 }
8932
8933 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8934                                  gpointer data)
8935 {
8936         gchar buf[3] = "3";
8937         Compose *compose = (Compose *)data;
8938         gsize bytes_read;
8939
8940         debug_print(_("Compose: input from monitoring process\n"));
8941
8942         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8943
8944         g_io_channel_shutdown(source, FALSE, NULL);
8945         g_io_channel_unref(source);
8946
8947         waitpid(compose->exteditor_pid, NULL, 0);
8948
8949         if (buf[0] == '0') {            /* success */
8950                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8951                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8952
8953                 gtk_text_buffer_set_text(buffer, "", -1);
8954                 compose_insert_file(compose, compose->exteditor_file);
8955                 compose_changed_cb(NULL, compose);
8956                 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8957
8958                 if (claws_unlink(compose->exteditor_file) < 0)
8959                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8960         } else if (buf[0] == '1') {     /* failed */
8961                 g_warning("Couldn't exec external editor\n");
8962                 if (claws_unlink(compose->exteditor_file) < 0)
8963                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8964         } else if (buf[0] == '2') {
8965                 g_warning("Couldn't write to file\n");
8966         } else if (buf[0] == '3') {
8967                 g_warning("Pipe read failed\n");
8968         }
8969
8970         compose_set_ext_editor_sensitive(compose, TRUE);
8971
8972         g_free(compose->exteditor_file);
8973         compose->exteditor_file    = NULL;
8974         compose->exteditor_pid     = -1;
8975         compose->exteditor_ch      = NULL;
8976         compose->exteditor_tag     = -1;
8977
8978         return FALSE;
8979 }
8980
8981 static void compose_set_ext_editor_sensitive(Compose *compose,
8982                                              gboolean sensitive)
8983 {
8984         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8985         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8986         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8987         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8988         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8989         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8990         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8991
8992         gtk_widget_set_sensitive(compose->text,                       sensitive);
8993         if (compose->toolbar->send_btn)
8994                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8995         if (compose->toolbar->sendl_btn)
8996                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8997         if (compose->toolbar->draft_btn)
8998                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8999         if (compose->toolbar->insert_btn)
9000                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9001         if (compose->toolbar->sig_btn)
9002                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9003         if (compose->toolbar->exteditor_btn)
9004                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9005         if (compose->toolbar->linewrap_current_btn)
9006                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9007         if (compose->toolbar->linewrap_all_btn)
9008                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9009 }
9010 #endif /* G_OS_UNIX */
9011
9012 /**
9013  * compose_undo_state_changed:
9014  *
9015  * Change the sensivity of the menuentries undo and redo
9016  **/
9017 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9018                                        gint redo_state, gpointer data)
9019 {
9020         Compose *compose = (Compose *)data;
9021
9022         switch (undo_state) {
9023         case UNDO_STATE_TRUE:
9024                 if (!undostruct->undo_state) {
9025                         undostruct->undo_state = TRUE;
9026                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9027                 }
9028                 break;
9029         case UNDO_STATE_FALSE:
9030                 if (undostruct->undo_state) {
9031                         undostruct->undo_state = FALSE;
9032                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9033                 }
9034                 break;
9035         case UNDO_STATE_UNCHANGED:
9036                 break;
9037         case UNDO_STATE_REFRESH:
9038                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9039                 break;
9040         default:
9041                 g_warning("Undo state not recognized");
9042                 break;
9043         }
9044
9045         switch (redo_state) {
9046         case UNDO_STATE_TRUE:
9047                 if (!undostruct->redo_state) {
9048                         undostruct->redo_state = TRUE;
9049                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9050                 }
9051                 break;
9052         case UNDO_STATE_FALSE:
9053                 if (undostruct->redo_state) {
9054                         undostruct->redo_state = FALSE;
9055                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9056                 }
9057                 break;
9058         case UNDO_STATE_UNCHANGED:
9059                 break;
9060         case UNDO_STATE_REFRESH:
9061                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9062                 break;
9063         default:
9064                 g_warning("Redo state not recognized");
9065                 break;
9066         }
9067 }
9068
9069 /* callback functions */
9070
9071 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9072  * includes "non-client" (windows-izm) in calculation, so this calculation
9073  * may not be accurate.
9074  */
9075 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9076                                         GtkAllocation *allocation,
9077                                         GtkSHRuler *shruler)
9078 {
9079         if (prefs_common.show_ruler) {
9080                 gint char_width = 0, char_height = 0;
9081                 gint line_width_in_chars;
9082
9083                 gtkut_get_font_size(GTK_WIDGET(widget),
9084                                     &char_width, &char_height);
9085                 line_width_in_chars =
9086                         (allocation->width - allocation->x) / char_width;
9087
9088                 /* got the maximum */
9089                 gtk_ruler_set_range(GTK_RULER(shruler),
9090                                     0.0, line_width_in_chars, 0,
9091                                     /*line_width_in_chars*/ char_width);
9092         }
9093
9094         return TRUE;
9095 }
9096
9097 typedef struct {
9098         gchar                   *header;
9099         gchar                   *entry;
9100         ComposePrefType         type;
9101         gboolean                entry_marked;
9102 } HeaderEntryState;
9103
9104 static void account_activated(GtkComboBox *optmenu, gpointer data)
9105 {
9106         Compose *compose = (Compose *)data;
9107
9108         PrefsAccount *ac;
9109         gchar *folderidentifier;
9110         gint account_id = 0;
9111         GtkTreeModel *menu;
9112         GtkTreeIter iter;
9113         GSList *list, *saved_list = NULL;
9114         HeaderEntryState *state;
9115         GtkRcStyle *style = NULL;
9116         static GdkColor yellow;
9117         static gboolean color_set = FALSE;
9118
9119         /* Get ID of active account in the combo box */
9120         menu = gtk_combo_box_get_model(optmenu);
9121         gtk_combo_box_get_active_iter(optmenu, &iter);
9122         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9123
9124         ac = account_find_from_id(account_id);
9125         cm_return_if_fail(ac != NULL);
9126
9127         if (ac != compose->account) {
9128                 compose_select_account(compose, ac, FALSE);
9129
9130                 for (list = compose->header_list; list; list = list->next) {
9131                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9132                         
9133                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9134                                 compose_destroy_headerentry(compose, hentry);
9135                                 continue;
9136                         }
9137                         
9138                         state = g_malloc0(sizeof(HeaderEntryState));
9139                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9140                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9141                         state->entry = gtk_editable_get_chars(
9142                                         GTK_EDITABLE(hentry->entry), 0, -1);
9143                         state->type = hentry->type;
9144                                 
9145                         if (!color_set) {
9146                                 gdk_color_parse("#f5f6be", &yellow);
9147                                 color_set = gdk_colormap_alloc_color(
9148                                                         gdk_colormap_get_system(),
9149                                                         &yellow, FALSE, TRUE);
9150                         }
9151                                 
9152                         style = gtk_widget_get_modifier_style(hentry->entry);
9153                         state->entry_marked = gdk_color_equal(&yellow,
9154                                                 &style->base[GTK_STATE_NORMAL]);
9155
9156                         saved_list = g_slist_append(saved_list, state);
9157                         compose_destroy_headerentry(compose, hentry);
9158                 }
9159
9160                 compose->header_last = NULL;
9161                 g_slist_free(compose->header_list);
9162                 compose->header_list = NULL;
9163                 compose->header_nextrow = 1;
9164                 compose_create_header_entry(compose);
9165                 
9166                 if (ac->set_autocc && ac->auto_cc)
9167                         compose_entry_append(compose, ac->auto_cc,
9168                                                 COMPOSE_CC, PREF_ACCOUNT);
9169
9170                 if (ac->set_autobcc && ac->auto_bcc) 
9171                         compose_entry_append(compose, ac->auto_bcc,
9172                                                 COMPOSE_BCC, PREF_ACCOUNT);
9173         
9174                 if (ac->set_autoreplyto && ac->auto_replyto)
9175                         compose_entry_append(compose, ac->auto_replyto,
9176                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9177                 
9178                 for (list = saved_list; list; list = list->next) {
9179                         state = (HeaderEntryState *) list->data;
9180                         
9181                         compose_add_header_entry(compose, state->header,
9182                                                 state->entry, state->type);
9183                         if (state->entry_marked)
9184                                 compose_entry_mark_default_to(compose, state->entry);
9185                         
9186                         g_free(state->header);  
9187                         g_free(state->entry);
9188                         g_free(state);
9189                 }
9190                 g_slist_free(saved_list);
9191                 
9192                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9193                                         (ac->protocol == A_NNTP) ? 
9194                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9195         }
9196
9197         /* Set message save folder */
9198         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9199                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9200         }
9201         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9202                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9203                            
9204         compose_set_save_to(compose, NULL);
9205         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9206                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9207                                   (compose->account, F_OUTBOX));
9208                 compose_set_save_to(compose, folderidentifier);
9209                 g_free(folderidentifier);
9210         }
9211 }
9212
9213 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9214                             GtkTreeViewColumn *column, Compose *compose)
9215 {
9216         compose_attach_property(NULL, compose);
9217 }
9218
9219 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9220                                       gpointer data)
9221 {
9222         Compose *compose = (Compose *)data;
9223         GtkTreeSelection *attach_selection;
9224         gint attach_nr_selected;
9225         
9226         if (!event) return FALSE;
9227
9228         if (event->button == 3) {
9229                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9230                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9231                         
9232                 if (attach_nr_selected > 0)
9233                 {
9234                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9235                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9236                 } else {
9237                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9238                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9239                 }
9240                         
9241                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9242                                NULL, NULL, event->button, event->time);
9243                 return TRUE;                           
9244         }
9245
9246         return FALSE;
9247 }
9248
9249 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9250                                    gpointer data)
9251 {
9252         Compose *compose = (Compose *)data;
9253
9254         if (!event) return FALSE;
9255
9256         switch (event->keyval) {
9257         case GDK_Delete:
9258                 compose_attach_remove_selected(NULL, compose);
9259                 break;
9260         }
9261         return FALSE;
9262 }
9263
9264 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9265 {
9266         toolbar_comp_set_sensitive(compose, allow);
9267         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9268         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9269 #if USE_ENCHANT
9270         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9271 #endif  
9272         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9273         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9274         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9275         
9276         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9277
9278 }
9279
9280 static void compose_send_cb(GtkAction *action, gpointer data)
9281 {
9282         Compose *compose = (Compose *)data;
9283
9284         if (prefs_common.work_offline && 
9285             !inc_offline_should_override(TRUE,
9286                 _("Claws Mail needs network access in order "
9287                   "to send this email.")))
9288                 return;
9289         
9290         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9291                 g_source_remove(compose->draft_timeout_tag);
9292                 compose->draft_timeout_tag = -1;
9293         }
9294
9295         compose_send(compose);
9296 }
9297
9298 static void compose_send_later_cb(GtkAction *action, gpointer data)
9299 {
9300         Compose *compose = (Compose *)data;
9301         gint val;
9302
9303         inc_lock();
9304         compose_allow_user_actions(compose, FALSE);
9305         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9306         compose_allow_user_actions(compose, TRUE);
9307         inc_unlock();
9308
9309         if (!val) {
9310                 compose_close(compose);
9311         } else if (val == -1) {
9312                 alertpanel_error(_("Could not queue message."));
9313         } else if (val == -2) {
9314                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9315         } else if (val == -3) {
9316                 if (privacy_peek_error())
9317                 alertpanel_error(_("Could not queue message for sending:\n\n"
9318                                    "Signature failed: %s"), privacy_get_error());
9319         } else if (val == -4) {
9320                 alertpanel_error(_("Could not queue message for sending:\n\n"
9321                                    "Charset conversion failed."));
9322         } else if (val == -5) {
9323                 alertpanel_error(_("Could not queue message for sending:\n\n"
9324                                    "Couldn't get recipient encryption key."));
9325         } else if (val == -6) {
9326                 /* silent error */
9327         }
9328         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9329 }
9330
9331 #define DRAFTED_AT_EXIT "drafted_at_exit"
9332 static void compose_register_draft(MsgInfo *info)
9333 {
9334         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9335                                       DRAFTED_AT_EXIT, NULL);
9336         FILE *fp = g_fopen(filepath, "ab");
9337         
9338         if (fp) {
9339                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
9340                                 info->msgnum);
9341                 fclose(fp);
9342         }
9343                 
9344         g_free(filepath);       
9345 }
9346
9347 gboolean compose_draft (gpointer data, guint action) 
9348 {
9349         Compose *compose = (Compose *)data;
9350         FolderItem *draft;
9351         gchar *tmp;
9352         gint msgnum;
9353         MsgFlags flag = {0, 0};
9354         static gboolean lock = FALSE;
9355         MsgInfo *newmsginfo;
9356         FILE *fp;
9357         gboolean target_locked = FALSE;
9358         gboolean err = FALSE;
9359
9360         if (lock) return FALSE;
9361
9362         if (compose->sending)
9363                 return TRUE;
9364
9365         draft = account_get_special_folder(compose->account, F_DRAFT);
9366         cm_return_val_if_fail(draft != NULL, FALSE);
9367         
9368         if (!g_mutex_trylock(compose->mutex)) {
9369                 /* we don't want to lock the mutex once it's available,
9370                  * because as the only other part of compose.c locking
9371                  * it is compose_close - which means once unlocked,
9372                  * the compose struct will be freed */
9373                 debug_print("couldn't lock mutex, probably sending\n");
9374                 return FALSE;
9375         }
9376         
9377         lock = TRUE;
9378
9379         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9380                               G_DIR_SEPARATOR, compose);
9381         if ((fp = g_fopen(tmp, "wb")) == NULL) {
9382                 FILE_OP_ERROR(tmp, "fopen");
9383                 goto warn_err;
9384         }
9385
9386         /* chmod for security */
9387         if (change_file_mode_rw(fp, tmp) < 0) {
9388                 FILE_OP_ERROR(tmp, "chmod");
9389                 g_warning("can't change file mode\n");
9390         }
9391
9392         /* Save draft infos */
9393         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9394         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9395
9396         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9397                 gchar *savefolderid;
9398
9399                 savefolderid = compose_get_save_to(compose);
9400                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9401                 g_free(savefolderid);
9402         }
9403         if (compose->return_receipt) {
9404                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9405         }
9406         if (compose->privacy_system) {
9407                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9408                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9409                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9410         }
9411
9412         /* Message-ID of message replying to */
9413         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9414                 gchar *folderid;
9415                 
9416                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9417                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9418                 g_free(folderid);
9419         }
9420         /* Message-ID of message forwarding to */
9421         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9422                 gchar *folderid;
9423                 
9424                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9425                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9426                 g_free(folderid);
9427         }
9428
9429         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9430         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9431
9432         /* end of headers */
9433         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9434
9435         if (err) {
9436                 fclose(fp);
9437                 goto warn_err;
9438         }
9439
9440         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9441                 fclose(fp);
9442                 goto warn_err;
9443         }
9444         if (fclose(fp) == EOF) {
9445                 goto warn_err;
9446         }
9447         
9448         if (compose->targetinfo) {
9449                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9450                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9451         }
9452         flag.tmp_flags = MSG_DRAFT;
9453
9454         folder_item_scan(draft);
9455         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9456                 MsgInfo *tmpinfo = NULL;
9457                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9458                 if (compose->msgid) {
9459                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9460                 }
9461                 if (tmpinfo) {
9462                         msgnum = tmpinfo->msgnum;
9463                         procmsg_msginfo_free(tmpinfo);
9464                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9465                 } else {
9466                         debug_print("didn't get draft msgnum after scanning\n");
9467                 }
9468         } else {
9469                 debug_print("got draft msgnum %d from adding\n", msgnum);
9470         }
9471         if (msgnum < 0) {
9472 warn_err:
9473                 claws_unlink(tmp);
9474                 g_free(tmp);
9475                 if (action != COMPOSE_AUTO_SAVE) {
9476                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9477                                 alertpanel_error(_("Could not save draft."));
9478                         else {
9479                                 AlertValue val;
9480                                 gtkut_window_popup(compose->window);
9481                                 val = alertpanel_full(_("Could not save draft"),
9482                                         _("Could not save draft.\n"
9483                                         "Do you want to cancel exit or discard this email?"),
9484                                           _("_Cancel exit"), _("_Discard email"), NULL,
9485                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9486                                 if (val == G_ALERTALTERNATE) {
9487                                         lock = FALSE;
9488                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9489                                         compose_close(compose);
9490                                         return TRUE;
9491                                 } else {
9492                                         lock = FALSE;
9493                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9494                                         return FALSE;
9495                                 }
9496                         }
9497                 }
9498                 goto unlock;
9499         }
9500         g_free(tmp);
9501
9502         if (compose->mode == COMPOSE_REEDIT) {
9503                 compose_remove_reedit_target(compose, TRUE);
9504         }
9505
9506         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9507
9508         if (newmsginfo) {
9509                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9510                 if (target_locked)
9511                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9512                 else
9513                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9514                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9515                         procmsg_msginfo_set_flags(newmsginfo, 0,
9516                                                   MSG_HAS_ATTACHMENT);
9517
9518                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9519                         compose_register_draft(newmsginfo);
9520                 }
9521                 procmsg_msginfo_free(newmsginfo);
9522         }
9523         
9524         folder_item_scan(draft);
9525         
9526         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9527                 lock = FALSE;
9528                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9529                 compose_close(compose);
9530                 return TRUE;
9531         } else {
9532                 struct stat s;
9533                 gchar *path;
9534
9535                 path = folder_item_fetch_msg(draft, msgnum);
9536                 if (path == NULL) {
9537                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9538                         goto unlock;
9539                 }
9540                 if (g_stat(path, &s) < 0) {
9541                         FILE_OP_ERROR(path, "stat");
9542                         g_free(path);
9543                         goto unlock;
9544                 }
9545                 g_free(path);
9546
9547                 procmsg_msginfo_free(compose->targetinfo);
9548                 compose->targetinfo = procmsg_msginfo_new();
9549                 compose->targetinfo->msgnum = msgnum;
9550                 compose->targetinfo->size = (goffset)s.st_size;
9551                 compose->targetinfo->mtime = s.st_mtime;
9552                 compose->targetinfo->folder = draft;
9553                 if (target_locked)
9554                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9555                 compose->mode = COMPOSE_REEDIT;
9556                 
9557                 if (action == COMPOSE_AUTO_SAVE) {
9558                         compose->autosaved_draft = compose->targetinfo;
9559                 }
9560                 compose->modified = FALSE;
9561                 compose_set_title(compose);
9562         }
9563 unlock:
9564         lock = FALSE;
9565         g_mutex_unlock(compose->mutex);
9566         return TRUE;
9567 }
9568
9569 void compose_clear_exit_drafts(void)
9570 {
9571         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9572                                       DRAFTED_AT_EXIT, NULL);
9573         if (is_file_exist(filepath))
9574                 claws_unlink(filepath);
9575         
9576         g_free(filepath);
9577 }
9578
9579 void compose_reopen_exit_drafts(void)
9580 {
9581         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9582                                       DRAFTED_AT_EXIT, NULL);
9583         FILE *fp = g_fopen(filepath, "rb");
9584         gchar buf[1024];
9585         
9586         if (fp) {
9587                 while (fgets(buf, sizeof(buf), fp)) {
9588                         gchar **parts = g_strsplit(buf, "\t", 2);
9589                         const gchar *folder = parts[0];
9590                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9591                         
9592                         if (folder && *folder && msgnum > -1) {
9593                                 FolderItem *item = folder_find_item_from_identifier(folder);
9594                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9595                                 if (info)
9596                                         compose_reedit(info, FALSE);
9597                         }
9598                         g_strfreev(parts);
9599                 }       
9600                 fclose(fp);
9601         }       
9602         g_free(filepath);
9603         compose_clear_exit_drafts();
9604 }
9605
9606 static void compose_save_cb(GtkAction *action, gpointer data)
9607 {
9608         Compose *compose = (Compose *)data;
9609         compose_draft(compose, COMPOSE_KEEP_EDITING);
9610         compose->rmode = COMPOSE_REEDIT;
9611 }
9612
9613 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9614 {
9615         if (compose && file_list) {
9616                 GList *tmp;
9617
9618                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9619                         gchar *file = (gchar *) tmp->data;
9620                         gchar *utf8_filename = conv_filename_to_utf8(file);
9621                         compose_attach_append(compose, file, utf8_filename, NULL);
9622                         compose_changed_cb(NULL, compose);
9623                         if (free_data) {
9624                         g_free(file);
9625                                 tmp->data = NULL;
9626                         }
9627                         g_free(utf8_filename);
9628                 }
9629         }
9630 }
9631
9632 static void compose_attach_cb(GtkAction *action, gpointer data)
9633 {
9634         Compose *compose = (Compose *)data;
9635         GList *file_list;
9636
9637         if (compose->redirect_filename != NULL)
9638                 return;
9639
9640         file_list = filesel_select_multiple_files_open(_("Select file"));
9641
9642         if (file_list) {
9643                 compose_attach_from_list(compose, file_list, TRUE);
9644                 g_list_free(file_list);
9645         }
9646 }
9647
9648 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9649 {
9650         Compose *compose = (Compose *)data;
9651         GList *file_list;
9652         gint files_inserted = 0;
9653
9654         file_list = filesel_select_multiple_files_open(_("Select file"));
9655
9656         if (file_list) {
9657                 GList *tmp;
9658
9659                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9660                         gchar *file = (gchar *) tmp->data;
9661                         gchar *filedup = g_strdup(file);
9662                         gchar *shortfile = g_path_get_basename(filedup);
9663                         ComposeInsertResult res;
9664                         /* insert the file if the file is short or if the user confirmed that
9665                            he/she wants to insert the large file */
9666                         res = compose_insert_file(compose, file);
9667                         if (res == COMPOSE_INSERT_READ_ERROR) {
9668                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9669                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9670                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9671                                                         "for the current encoding, insertion may be incorrect."),
9672                                                         shortfile);
9673                         } else if (res == COMPOSE_INSERT_SUCCESS)
9674                                 files_inserted++;
9675
9676                         g_free(shortfile);
9677                         g_free(filedup);
9678                         g_free(file);
9679                 }
9680                 g_list_free(file_list);
9681         }
9682
9683 #ifdef USE_ENCHANT      
9684         if (files_inserted > 0 && compose->gtkaspell && 
9685             compose->gtkaspell->check_while_typing)
9686                 gtkaspell_highlight_all(compose->gtkaspell);
9687 #endif
9688 }
9689
9690 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9691 {
9692         Compose *compose = (Compose *)data;
9693
9694         compose_insert_sig(compose, FALSE);
9695 }
9696
9697 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9698                               gpointer data)
9699 {
9700         gint x, y;
9701         Compose *compose = (Compose *)data;
9702
9703         gtkut_widget_get_uposition(widget, &x, &y);
9704         if (!compose->batch) {
9705                 prefs_common.compose_x = x;
9706                 prefs_common.compose_y = y;
9707         }
9708         if (compose->sending || compose->updating)
9709                 return TRUE;
9710         compose_close_cb(NULL, compose);
9711         return TRUE;
9712 }
9713
9714 void compose_close_toolbar(Compose *compose)
9715 {
9716         compose_close_cb(NULL, compose);
9717 }
9718
9719 static void compose_close_cb(GtkAction *action, gpointer data)
9720 {
9721         Compose *compose = (Compose *)data;
9722         AlertValue val;
9723
9724 #ifdef G_OS_UNIX
9725         if (compose->exteditor_tag != -1) {
9726                 if (!compose_ext_editor_kill(compose))
9727                         return;
9728         }
9729 #endif
9730
9731         if (compose->modified) {
9732                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9733                 if (!g_mutex_trylock(compose->mutex)) {
9734                         /* we don't want to lock the mutex once it's available,
9735                          * because as the only other part of compose.c locking
9736                          * it is compose_close - which means once unlocked,
9737                          * the compose struct will be freed */
9738                         debug_print("couldn't lock mutex, probably sending\n");
9739                         return;
9740                 }
9741                 if (!reedit) {
9742                         val = alertpanel(_("Discard message"),
9743                                  _("This message has been modified. Discard it?"),
9744                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9745                 } else {
9746                         val = alertpanel(_("Save changes"),
9747                                  _("This message has been modified. Save the latest changes?"),
9748                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9749                 }
9750                 g_mutex_unlock(compose->mutex);
9751                 switch (val) {
9752                 case G_ALERTDEFAULT:
9753                         if (prefs_common.autosave && !reedit)
9754                                 compose_remove_draft(compose);                  
9755                         break;
9756                 case G_ALERTALTERNATE:
9757                         compose_draft(data, COMPOSE_QUIT_EDITING);
9758                         return;
9759                 default:
9760                         return;
9761                 }
9762         }
9763
9764         compose_close(compose);
9765 }
9766
9767 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9768 {
9769         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9770         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9771         Compose *compose = (Compose *) data;
9772
9773         if (active)
9774                 compose->out_encoding = (CharSet)value;
9775 }
9776
9777 static void compose_address_cb(GtkAction *action, gpointer data)
9778 {
9779         Compose *compose = (Compose *)data;
9780
9781         addressbook_open(compose);
9782 }
9783
9784 static void about_show_cb(GtkAction *action, gpointer data)
9785 {
9786         about_show();
9787 }
9788
9789 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9790 {
9791         Compose *compose = (Compose *)data;
9792         Template *tmpl;
9793         gchar *msg;
9794         AlertValue val;
9795
9796         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9797         cm_return_if_fail(tmpl != NULL);
9798
9799         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9800                               tmpl->name);
9801         val = alertpanel(_("Apply template"), msg,
9802                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9803         g_free(msg);
9804
9805         if (val == G_ALERTDEFAULT)
9806                 compose_template_apply(compose, tmpl, TRUE);
9807         else if (val == G_ALERTALTERNATE)
9808                 compose_template_apply(compose, tmpl, FALSE);
9809 }
9810
9811 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9812 {
9813         Compose *compose = (Compose *)data;
9814
9815         compose_exec_ext_editor(compose);
9816 }
9817
9818 static void compose_undo_cb(GtkAction *action, gpointer data)
9819 {
9820         Compose *compose = (Compose *)data;
9821         gboolean prev_autowrap = compose->autowrap;
9822
9823         compose->autowrap = FALSE;
9824         undo_undo(compose->undostruct);
9825         compose->autowrap = prev_autowrap;
9826 }
9827
9828 static void compose_redo_cb(GtkAction *action, gpointer data)
9829 {
9830         Compose *compose = (Compose *)data;
9831         gboolean prev_autowrap = compose->autowrap;
9832         
9833         compose->autowrap = FALSE;
9834         undo_redo(compose->undostruct);
9835         compose->autowrap = prev_autowrap;
9836 }
9837
9838 static void entry_cut_clipboard(GtkWidget *entry)
9839 {
9840         if (GTK_IS_EDITABLE(entry))
9841                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9842         else if (GTK_IS_TEXT_VIEW(entry))
9843                 gtk_text_buffer_cut_clipboard(
9844                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9845                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9846                         TRUE);
9847 }
9848
9849 static void entry_copy_clipboard(GtkWidget *entry)
9850 {
9851         if (GTK_IS_EDITABLE(entry))
9852                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9853         else if (GTK_IS_TEXT_VIEW(entry))
9854                 gtk_text_buffer_copy_clipboard(
9855                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9856                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9857 }
9858
9859 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9860                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9861 {
9862         if (GTK_IS_TEXT_VIEW(entry)) {
9863                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9864                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9865                 GtkTextIter start_iter, end_iter;
9866                 gint start, end;
9867                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9868
9869                 if (contents == NULL)
9870                         return;
9871         
9872                 /* we shouldn't delete the selection when middle-click-pasting, or we
9873                  * can't mid-click-paste our own selection */
9874                 if (clip != GDK_SELECTION_PRIMARY) {
9875                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9876                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9877                 }
9878                 
9879                 if (insert_place == NULL) {
9880                         /* if insert_place isn't specified, insert at the cursor.
9881                          * used for Ctrl-V pasting */
9882                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9883                         start = gtk_text_iter_get_offset(&start_iter);
9884                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9885                 } else {
9886                         /* if insert_place is specified, paste here.
9887                          * used for mid-click-pasting */
9888                         start = gtk_text_iter_get_offset(insert_place);
9889                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9890                         if (prefs_common.primary_paste_unselects)
9891                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9892                 }
9893                 
9894                 if (!wrap) {
9895                         /* paste unwrapped: mark the paste so it's not wrapped later */
9896                         end = start + strlen(contents);
9897                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9898                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9899                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9900                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9901                         /* rewrap paragraph now (after a mid-click-paste) */
9902                         mark_start = gtk_text_buffer_get_insert(buffer);
9903                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9904                         gtk_text_iter_backward_char(&start_iter);
9905                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9906                 }
9907         } else if (GTK_IS_EDITABLE(entry))
9908                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9909
9910         compose->modified = TRUE;
9911 }
9912
9913 static void entry_allsel(GtkWidget *entry)
9914 {
9915         if (GTK_IS_EDITABLE(entry))
9916                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9917         else if (GTK_IS_TEXT_VIEW(entry)) {
9918                 GtkTextIter startiter, enditer;
9919                 GtkTextBuffer *textbuf;
9920
9921                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9922                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9923                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9924
9925                 gtk_text_buffer_move_mark_by_name(textbuf, 
9926                         "selection_bound", &startiter);
9927                 gtk_text_buffer_move_mark_by_name(textbuf, 
9928                         "insert", &enditer);
9929         }
9930 }
9931
9932 static void compose_cut_cb(GtkAction *action, gpointer data)
9933 {
9934         Compose *compose = (Compose *)data;
9935         if (compose->focused_editable 
9936 #ifndef GENERIC_UMPC
9937             && gtkut_widget_has_focus(compose->focused_editable)
9938 #endif
9939             )
9940                 entry_cut_clipboard(compose->focused_editable);
9941 }
9942
9943 static void compose_copy_cb(GtkAction *action, gpointer data)
9944 {
9945         Compose *compose = (Compose *)data;
9946         if (compose->focused_editable 
9947 #ifndef GENERIC_UMPC
9948             && gtkut_widget_has_focus(compose->focused_editable)
9949 #endif
9950             )
9951                 entry_copy_clipboard(compose->focused_editable);
9952 }
9953
9954 static void compose_paste_cb(GtkAction *action, gpointer data)
9955 {
9956         Compose *compose = (Compose *)data;
9957         gint prev_autowrap;
9958         GtkTextBuffer *buffer;
9959         BLOCK_WRAP();
9960         if (compose->focused_editable &&
9961             gtkut_widget_has_focus(compose->focused_editable))
9962                 entry_paste_clipboard(compose, compose->focused_editable, 
9963                                 prefs_common.linewrap_pastes,
9964                                 GDK_SELECTION_CLIPBOARD, NULL);
9965         UNBLOCK_WRAP();
9966
9967 #ifdef USE_ENCHANT
9968         if (gtkut_widget_has_focus(compose->text) &&
9969             compose->gtkaspell && 
9970             compose->gtkaspell->check_while_typing)
9971                 gtkaspell_highlight_all(compose->gtkaspell);
9972 #endif
9973 }
9974
9975 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9976 {
9977         Compose *compose = (Compose *)data;
9978         gint wrap_quote = prefs_common.linewrap_quote;
9979         if (compose->focused_editable 
9980 #ifndef GENERIC_UMPC
9981             && gtkut_widget_has_focus(compose->focused_editable)
9982 #endif
9983             ) {
9984                 /* let text_insert() (called directly or at a later time
9985                  * after the gtk_editable_paste_clipboard) know that 
9986                  * text is to be inserted as a quotation. implemented
9987                  * by using a simple refcount... */
9988                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9989                                                 G_OBJECT(compose->focused_editable),
9990                                                 "paste_as_quotation"));
9991                 g_object_set_data(G_OBJECT(compose->focused_editable),
9992                                     "paste_as_quotation",
9993                                     GINT_TO_POINTER(paste_as_quotation + 1));
9994                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9995                 entry_paste_clipboard(compose, compose->focused_editable, 
9996                                 prefs_common.linewrap_pastes,
9997                                 GDK_SELECTION_CLIPBOARD, NULL);
9998                 prefs_common.linewrap_quote = wrap_quote;
9999         }
10000 }
10001
10002 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10003 {
10004         Compose *compose = (Compose *)data;
10005         gint prev_autowrap;
10006         GtkTextBuffer *buffer;
10007         BLOCK_WRAP();
10008         if (compose->focused_editable 
10009 #ifndef GENERIC_UMPC
10010             && gtkut_widget_has_focus(compose->focused_editable)
10011 #endif
10012             )
10013                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10014                         GDK_SELECTION_CLIPBOARD, NULL);
10015         UNBLOCK_WRAP();
10016
10017 #ifdef USE_ENCHANT
10018         if (gtkut_widget_has_focus(compose->text) &&
10019             compose->gtkaspell && 
10020             compose->gtkaspell->check_while_typing)
10021                 gtkaspell_highlight_all(compose->gtkaspell);
10022 #endif
10023 }
10024
10025 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10026 {
10027         Compose *compose = (Compose *)data;
10028         gint prev_autowrap;
10029         GtkTextBuffer *buffer;
10030         BLOCK_WRAP();
10031         if (compose->focused_editable 
10032 #ifndef GENERIC_UMPC
10033             && gtkut_widget_has_focus(compose->focused_editable)
10034 #endif
10035             )
10036                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10037                         GDK_SELECTION_CLIPBOARD, NULL);
10038         UNBLOCK_WRAP();
10039
10040 #ifdef USE_ENCHANT
10041         if (gtkut_widget_has_focus(compose->text) &&
10042             compose->gtkaspell &&
10043             compose->gtkaspell->check_while_typing)
10044                 gtkaspell_highlight_all(compose->gtkaspell);
10045 #endif
10046 }
10047
10048 static void compose_allsel_cb(GtkAction *action, gpointer data)
10049 {
10050         Compose *compose = (Compose *)data;
10051         if (compose->focused_editable 
10052 #ifndef GENERIC_UMPC
10053             && gtkut_widget_has_focus(compose->focused_editable)
10054 #endif
10055             )
10056                 entry_allsel(compose->focused_editable);
10057 }
10058
10059 static void textview_move_beginning_of_line (GtkTextView *text)
10060 {
10061         GtkTextBuffer *buffer;
10062         GtkTextMark *mark;
10063         GtkTextIter ins;
10064
10065         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10066
10067         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10068         mark = gtk_text_buffer_get_insert(buffer);
10069         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10070         gtk_text_iter_set_line_offset(&ins, 0);
10071         gtk_text_buffer_place_cursor(buffer, &ins);
10072 }
10073
10074 static void textview_move_forward_character (GtkTextView *text)
10075 {
10076         GtkTextBuffer *buffer;
10077         GtkTextMark *mark;
10078         GtkTextIter ins;
10079
10080         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10081
10082         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10083         mark = gtk_text_buffer_get_insert(buffer);
10084         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10085         if (gtk_text_iter_forward_cursor_position(&ins))
10086                 gtk_text_buffer_place_cursor(buffer, &ins);
10087 }
10088
10089 static void textview_move_backward_character (GtkTextView *text)
10090 {
10091         GtkTextBuffer *buffer;
10092         GtkTextMark *mark;
10093         GtkTextIter ins;
10094
10095         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10096
10097         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10098         mark = gtk_text_buffer_get_insert(buffer);
10099         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10100         if (gtk_text_iter_backward_cursor_position(&ins))
10101                 gtk_text_buffer_place_cursor(buffer, &ins);
10102 }
10103
10104 static void textview_move_forward_word (GtkTextView *text)
10105 {
10106         GtkTextBuffer *buffer;
10107         GtkTextMark *mark;
10108         GtkTextIter ins;
10109         gint count;
10110
10111         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10112
10113         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10114         mark = gtk_text_buffer_get_insert(buffer);
10115         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10116         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10117         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10118                 gtk_text_iter_backward_word_start(&ins);
10119                 gtk_text_buffer_place_cursor(buffer, &ins);
10120         }
10121 }
10122
10123 static void textview_move_backward_word (GtkTextView *text)
10124 {
10125         GtkTextBuffer *buffer;
10126         GtkTextMark *mark;
10127         GtkTextIter ins;
10128         gint count;
10129
10130         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10131
10132         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10133         mark = gtk_text_buffer_get_insert(buffer);
10134         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10135         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10136         if (gtk_text_iter_backward_word_starts(&ins, 1))
10137                 gtk_text_buffer_place_cursor(buffer, &ins);
10138 }
10139
10140 static void textview_move_end_of_line (GtkTextView *text)
10141 {
10142         GtkTextBuffer *buffer;
10143         GtkTextMark *mark;
10144         GtkTextIter ins;
10145
10146         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10147
10148         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10149         mark = gtk_text_buffer_get_insert(buffer);
10150         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10151         if (gtk_text_iter_forward_to_line_end(&ins))
10152                 gtk_text_buffer_place_cursor(buffer, &ins);
10153 }
10154
10155 static void textview_move_next_line (GtkTextView *text)
10156 {
10157         GtkTextBuffer *buffer;
10158         GtkTextMark *mark;
10159         GtkTextIter ins;
10160         gint offset;
10161
10162         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10163
10164         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10165         mark = gtk_text_buffer_get_insert(buffer);
10166         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10167         offset = gtk_text_iter_get_line_offset(&ins);
10168         if (gtk_text_iter_forward_line(&ins)) {
10169                 gtk_text_iter_set_line_offset(&ins, offset);
10170                 gtk_text_buffer_place_cursor(buffer, &ins);
10171         }
10172 }
10173
10174 static void textview_move_previous_line (GtkTextView *text)
10175 {
10176         GtkTextBuffer *buffer;
10177         GtkTextMark *mark;
10178         GtkTextIter ins;
10179         gint offset;
10180
10181         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10182
10183         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10184         mark = gtk_text_buffer_get_insert(buffer);
10185         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10186         offset = gtk_text_iter_get_line_offset(&ins);
10187         if (gtk_text_iter_backward_line(&ins)) {
10188                 gtk_text_iter_set_line_offset(&ins, offset);
10189                 gtk_text_buffer_place_cursor(buffer, &ins);
10190         }
10191 }
10192
10193 static void textview_delete_forward_character (GtkTextView *text)
10194 {
10195         GtkTextBuffer *buffer;
10196         GtkTextMark *mark;
10197         GtkTextIter ins, end_iter;
10198
10199         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10200
10201         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10202         mark = gtk_text_buffer_get_insert(buffer);
10203         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10204         end_iter = ins;
10205         if (gtk_text_iter_forward_char(&end_iter)) {
10206                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10207         }
10208 }
10209
10210 static void textview_delete_backward_character (GtkTextView *text)
10211 {
10212         GtkTextBuffer *buffer;
10213         GtkTextMark *mark;
10214         GtkTextIter ins, end_iter;
10215
10216         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10217
10218         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10219         mark = gtk_text_buffer_get_insert(buffer);
10220         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10221         end_iter = ins;
10222         if (gtk_text_iter_backward_char(&end_iter)) {
10223                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10224         }
10225 }
10226
10227 static void textview_delete_forward_word (GtkTextView *text)
10228 {
10229         GtkTextBuffer *buffer;
10230         GtkTextMark *mark;
10231         GtkTextIter ins, end_iter;
10232
10233         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10234
10235         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10236         mark = gtk_text_buffer_get_insert(buffer);
10237         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10238         end_iter = ins;
10239         if (gtk_text_iter_forward_word_end(&end_iter)) {
10240                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10241         }
10242 }
10243
10244 static void textview_delete_backward_word (GtkTextView *text)
10245 {
10246         GtkTextBuffer *buffer;
10247         GtkTextMark *mark;
10248         GtkTextIter ins, end_iter;
10249
10250         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10251
10252         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10253         mark = gtk_text_buffer_get_insert(buffer);
10254         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10255         end_iter = ins;
10256         if (gtk_text_iter_backward_word_start(&end_iter)) {
10257                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10258         }
10259 }
10260
10261 static void textview_delete_line (GtkTextView *text)
10262 {
10263         GtkTextBuffer *buffer;
10264         GtkTextMark *mark;
10265         GtkTextIter ins, start_iter, end_iter;
10266
10267         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10268
10269         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10270         mark = gtk_text_buffer_get_insert(buffer);
10271         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10272
10273         start_iter = ins;
10274         gtk_text_iter_set_line_offset(&start_iter, 0);
10275
10276         end_iter = ins;
10277         if (gtk_text_iter_ends_line(&end_iter)){
10278                 if (!gtk_text_iter_forward_char(&end_iter))
10279                         gtk_text_iter_backward_char(&start_iter);
10280         }
10281         else 
10282                 gtk_text_iter_forward_to_line_end(&end_iter);
10283         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10284 }
10285
10286 static void textview_delete_to_line_end (GtkTextView *text)
10287 {
10288         GtkTextBuffer *buffer;
10289         GtkTextMark *mark;
10290         GtkTextIter ins, end_iter;
10291
10292         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10293
10294         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10295         mark = gtk_text_buffer_get_insert(buffer);
10296         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10297         end_iter = ins;
10298         if (gtk_text_iter_ends_line(&end_iter))
10299                 gtk_text_iter_forward_char(&end_iter);
10300         else
10301                 gtk_text_iter_forward_to_line_end(&end_iter);
10302         gtk_text_buffer_delete(buffer, &ins, &end_iter);
10303 }
10304
10305 #define DO_ACTION(name, act) {                                          \
10306         if(!strcmp(name, a_name)) {                                     \
10307                 return act;                                             \
10308         }                                                               \
10309 }
10310 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10311 {
10312         const gchar *a_name = gtk_action_get_name(action);
10313         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10314         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10315         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10316         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10317         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10318         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10319         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10320         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10321         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10322         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10323         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10324         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10325         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10326         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10327         return -1;
10328 }
10329
10330 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10331 {
10332         Compose *compose = (Compose *)data;
10333         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10334         ComposeCallAdvancedAction action = -1;
10335         
10336         action = compose_call_advanced_action_from_path(gaction);
10337
10338         static struct {
10339                 void (*do_action) (GtkTextView *text);
10340         } action_table[] = {
10341                 {textview_move_beginning_of_line},
10342                 {textview_move_forward_character},
10343                 {textview_move_backward_character},
10344                 {textview_move_forward_word},
10345                 {textview_move_backward_word},
10346                 {textview_move_end_of_line},
10347                 {textview_move_next_line},
10348                 {textview_move_previous_line},
10349                 {textview_delete_forward_character},
10350                 {textview_delete_backward_character},
10351                 {textview_delete_forward_word},
10352                 {textview_delete_backward_word},
10353                 {textview_delete_line},
10354                 {textview_delete_to_line_end}
10355         };
10356
10357         if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10358
10359         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10360             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10361                 if (action_table[action].do_action)
10362                         action_table[action].do_action(text);
10363                 else
10364                         g_warning("Not implemented yet.");
10365         }
10366 }
10367
10368 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10369 {
10370         gchar *str = NULL;
10371         
10372         if (GTK_IS_EDITABLE(widget)) {
10373                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10374                 gtk_editable_set_position(GTK_EDITABLE(widget), 
10375                         strlen(str));
10376                 g_free(str);
10377                 if (widget->parent && widget->parent->parent
10378                  && widget->parent->parent->parent) {
10379                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10380                                 gint y = widget->allocation.y;
10381                                 gint height = widget->allocation.height;
10382                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10383                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10384
10385                                 if (y < (int)shown->value) {
10386                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10387                                 }
10388                                 if (y + height > (int)shown->value + (int)shown->page_size) {
10389                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10390                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10391                                                         y + height - (int)shown->page_size - 1);
10392                                         } else {
10393                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
10394                                                         (int)shown->upper - (int)shown->page_size - 1);
10395                                         }
10396                                 }
10397                         }
10398                 }
10399         }
10400
10401         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10402                 compose->focused_editable = widget;
10403         
10404 #ifdef GENERIC_UMPC
10405         if (GTK_IS_TEXT_VIEW(widget) 
10406             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10407                 g_object_ref(compose->notebook);
10408                 g_object_ref(compose->edit_vbox);
10409                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10410                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10411                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10412                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10413                 g_object_unref(compose->notebook);
10414                 g_object_unref(compose->edit_vbox);
10415                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10416                                         G_CALLBACK(compose_grab_focus_cb),
10417                                         compose);
10418                 gtk_widget_grab_focus(widget);
10419                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10420                                         G_CALLBACK(compose_grab_focus_cb),
10421                                         compose);
10422         } else if (!GTK_IS_TEXT_VIEW(widget) 
10423                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10424                 g_object_ref(compose->notebook);
10425                 g_object_ref(compose->edit_vbox);
10426                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10427                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10428                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10429                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10430                 g_object_unref(compose->notebook);
10431                 g_object_unref(compose->edit_vbox);
10432                 g_signal_handlers_block_by_func(G_OBJECT(widget),
10433                                         G_CALLBACK(compose_grab_focus_cb),
10434                                         compose);
10435                 gtk_widget_grab_focus(widget);
10436                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10437                                         G_CALLBACK(compose_grab_focus_cb),
10438                                         compose);
10439         }
10440 #endif
10441 }
10442
10443 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10444 {
10445         compose->modified = TRUE;
10446 //      compose_beautify_paragraph(compose, NULL, TRUE);
10447 #ifndef GENERIC_UMPC
10448         compose_set_title(compose);
10449 #endif
10450 }
10451
10452 static void compose_wrap_cb(GtkAction *action, gpointer data)
10453 {
10454         Compose *compose = (Compose *)data;
10455         compose_beautify_paragraph(compose, NULL, TRUE);
10456 }
10457
10458 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10459 {
10460         Compose *compose = (Compose *)data;
10461         compose_wrap_all_full(compose, TRUE);
10462 }
10463
10464 static void compose_find_cb(GtkAction *action, gpointer data)
10465 {
10466         Compose *compose = (Compose *)data;
10467
10468         message_search_compose(compose);
10469 }
10470
10471 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10472                                          gpointer        data)
10473 {
10474         Compose *compose = (Compose *)data;
10475         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10476         if (compose->autowrap)
10477                 compose_wrap_all_full(compose, TRUE);
10478         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10479 }
10480
10481 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10482                                          gpointer        data)
10483 {
10484         Compose *compose = (Compose *)data;
10485         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10486 }
10487
10488 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10489 {
10490         Compose *compose = (Compose *)data;
10491
10492         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10493 }
10494
10495 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10496 {
10497         Compose *compose = (Compose *)data;
10498
10499         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10500 }
10501
10502 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10503 {
10504         g_free(compose->privacy_system);
10505
10506         compose->privacy_system = g_strdup(account->default_privacy_system);
10507         compose_update_privacy_system_menu_item(compose, warn);
10508 }
10509
10510 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10511 {
10512         Compose *compose = (Compose *)data;
10513
10514         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10515                 gtk_widget_show(compose->ruler_hbox);
10516                 prefs_common.show_ruler = TRUE;
10517         } else {
10518                 gtk_widget_hide(compose->ruler_hbox);
10519                 gtk_widget_queue_resize(compose->edit_vbox);
10520                 prefs_common.show_ruler = FALSE;
10521         }
10522 }
10523
10524 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10525                                              GdkDragContext     *context,
10526                                              gint                x,
10527                                              gint                y,
10528                                              GtkSelectionData   *data,
10529                                              guint               info,
10530                                              guint               time,
10531                                              gpointer            user_data)
10532 {
10533         Compose *compose = (Compose *)user_data;
10534         GList *list, *tmp;
10535
10536         if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10537 #ifdef G_OS_WIN32
10538          || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10539 #endif
10540            ) && gtk_drag_get_source_widget(context) != 
10541                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10542                 list = uri_list_extract_filenames((const gchar *)data->data);
10543                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10544                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10545                         compose_attach_append
10546                                 (compose, (const gchar *)tmp->data,
10547                                  utf8_filename, NULL);
10548                         g_free(utf8_filename);
10549                 }
10550                 if (list) compose_changed_cb(NULL, compose);
10551                 list_free_strings(list);
10552                 g_list_free(list);
10553         } else if (gtk_drag_get_source_widget(context) 
10554                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10555                 /* comes from our summaryview */
10556                 SummaryView * summaryview = NULL;
10557                 GSList * list = NULL, *cur = NULL;
10558                 
10559                 if (mainwindow_get_mainwindow())
10560                         summaryview = mainwindow_get_mainwindow()->summaryview;
10561                 
10562                 if (summaryview)
10563                         list = summary_get_selected_msg_list(summaryview);
10564                 
10565                 for (cur = list; cur; cur = cur->next) {
10566                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10567                         gchar *file = NULL;
10568                         if (msginfo)
10569                                 file = procmsg_get_message_file_full(msginfo, 
10570                                         TRUE, TRUE);
10571                         if (file) {
10572                                 compose_attach_append(compose, (const gchar *)file, 
10573                                         (const gchar *)file, "message/rfc822");
10574                                 g_free(file);
10575                         }
10576                 }
10577                 g_slist_free(list);
10578         }
10579 }
10580
10581 static gboolean compose_drag_drop(GtkWidget *widget,
10582                                   GdkDragContext *drag_context,
10583                                   gint x, gint y,
10584                                   guint time, gpointer user_data)
10585 {
10586         /* not handling this signal makes compose_insert_drag_received_cb
10587          * called twice */
10588         return TRUE;                                     
10589 }
10590
10591 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10592                                              GdkDragContext     *drag_context,
10593                                              gint                x,
10594                                              gint                y,
10595                                              GtkSelectionData   *data,
10596                                              guint               info,
10597                                              guint               time,
10598                                              gpointer            user_data)
10599 {
10600         Compose *compose = (Compose *)user_data;
10601         GList *list, *tmp;
10602
10603         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10604          * does not work */
10605 #ifndef G_OS_WIN32
10606         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10607 #else
10608         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10609 #endif
10610                 AlertValue val = G_ALERTDEFAULT;
10611
10612                 list = uri_list_extract_filenames((const gchar *)data->data);
10613                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10614                         /* Assume a list of no files, and data has ://, is a remote link */
10615                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10616                         gchar *tmpfile = get_tmp_file();
10617                         str_write_to_file(tmpdata, tmpfile);
10618                         g_free(tmpdata);  
10619                         compose_insert_file(compose, tmpfile);
10620                         claws_unlink(tmpfile);
10621                         g_free(tmpfile);
10622                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10623                         compose_beautify_paragraph(compose, NULL, TRUE);
10624                         return;
10625                 }
10626                 switch (prefs_common.compose_dnd_mode) {
10627                         case COMPOSE_DND_ASK:
10628                                 val = alertpanel_full(_("Insert or attach?"),
10629                                          _("Do you want to insert the contents of the file(s) "
10630                                            "into the message body, or attach it to the email?"),
10631                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10632                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10633                                 break;
10634                         case COMPOSE_DND_INSERT:
10635                                 val = G_ALERTALTERNATE;
10636                                 break;
10637                         case COMPOSE_DND_ATTACH:
10638                                 val = G_ALERTOTHER;
10639                                 break;
10640                         default:
10641                                 /* unexpected case */
10642                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10643                 }
10644
10645                 if (val & G_ALERTDISABLE) {
10646                         val &= ~G_ALERTDISABLE;
10647                         /* remember what action to perform by default, only if we don't click Cancel */
10648                         if (val == G_ALERTALTERNATE)
10649                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10650                         else if (val == G_ALERTOTHER)
10651                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10652                 }
10653
10654                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10655                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10656                         list_free_strings(list);
10657                         g_list_free(list);
10658                         return;
10659                 } else if (val == G_ALERTOTHER) {
10660                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10661                         list_free_strings(list);
10662                         g_list_free(list);
10663                         return;
10664                 } 
10665
10666                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10667                         compose_insert_file(compose, (const gchar *)tmp->data);
10668                 }
10669                 list_free_strings(list);
10670                 g_list_free(list);
10671                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10672                 return;
10673         } else {
10674                 return;
10675         }
10676         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10677 }
10678
10679 static void compose_header_drag_received_cb (GtkWidget          *widget,
10680                                              GdkDragContext     *drag_context,
10681                                              gint                x,
10682                                              gint                y,
10683                                              GtkSelectionData   *data,
10684                                              guint               info,
10685                                              guint               time,
10686                                              gpointer            user_data)
10687 {
10688         GtkEditable *entry = (GtkEditable *)user_data;
10689         gchar *email = (gchar *)data->data;
10690
10691         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10692          * does not work */
10693
10694         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10695                 gchar *decoded=g_new(gchar, strlen(email));
10696                 int start = 0;
10697
10698                 email += strlen("mailto:");
10699                 decode_uri(decoded, email); /* will fit */
10700                 gtk_editable_delete_text(entry, 0, -1);
10701                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10702                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10703                 g_free(decoded);
10704                 return;
10705         }
10706         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10707 }
10708
10709 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10710 {
10711         Compose *compose = (Compose *)data;
10712
10713         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10714                 compose->return_receipt = TRUE;
10715         else
10716                 compose->return_receipt = FALSE;
10717 }
10718
10719 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10720 {
10721         Compose *compose = (Compose *)data;
10722
10723         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10724                 compose->remove_references = TRUE;
10725         else
10726                 compose->remove_references = FALSE;
10727 }
10728
10729 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10730                                         ComposeHeaderEntry *headerentry)
10731 {
10732         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10733         return FALSE;
10734 }
10735
10736 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10737                                             GdkEventKey *event,
10738                                             ComposeHeaderEntry *headerentry)
10739 {
10740         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10741             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10742             !(event->state & GDK_MODIFIER_MASK) &&
10743             (event->keyval == GDK_BackSpace) &&
10744             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10745                 gtk_container_remove
10746                         (GTK_CONTAINER(headerentry->compose->header_table),
10747                          headerentry->combo);
10748                 gtk_container_remove
10749                         (GTK_CONTAINER(headerentry->compose->header_table),
10750                          headerentry->entry);
10751                 headerentry->compose->header_list =
10752                         g_slist_remove(headerentry->compose->header_list,
10753                                        headerentry);
10754                 g_free(headerentry);
10755         } else  if (event->keyval == GDK_Tab) {
10756                 if (headerentry->compose->header_last == headerentry) {
10757                         /* Override default next focus, and give it to subject_entry
10758                          * instead of notebook tabs
10759                          */
10760                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10761                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10762                         return TRUE;
10763                 }
10764         }
10765         return FALSE;
10766 }
10767
10768 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10769                                     ComposeHeaderEntry *headerentry)
10770 {
10771         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10772                 compose_create_header_entry(headerentry->compose);
10773                 g_signal_handlers_disconnect_matched
10774                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10775                          0, 0, NULL, NULL, headerentry);
10776                 
10777                 /* Automatically scroll down */
10778                 GTK_EVENTS_FLUSH();
10779                 compose_show_first_last_header(headerentry->compose, FALSE);
10780                 
10781         }
10782         return FALSE;
10783 }
10784
10785 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10786 {
10787         GtkAdjustment *vadj;
10788
10789         cm_return_if_fail(compose);
10790         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10791         cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10792         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10793         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10794         gtk_adjustment_changed(vadj);
10795 }
10796
10797 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10798                           const gchar *text, gint len, Compose *compose)
10799 {
10800         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10801                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10802         GtkTextMark *mark;
10803
10804         cm_return_if_fail(text != NULL);
10805
10806         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10807                                         G_CALLBACK(text_inserted),
10808                                         compose);
10809         if (paste_as_quotation) {
10810                 gchar *new_text;
10811                 const gchar *qmark;
10812                 guint pos = 0;
10813                 GtkTextIter start_iter;
10814
10815                 if (len < 0)
10816                         len = strlen(text);
10817
10818                 new_text = g_strndup(text, len);
10819
10820                 qmark = compose_quote_char_from_context(compose);
10821
10822                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10823                 gtk_text_buffer_place_cursor(buffer, iter);
10824
10825                 pos = gtk_text_iter_get_offset(iter);
10826
10827                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10828                                                   _("Quote format error at line %d."));
10829                 quote_fmt_reset_vartable();
10830                 g_free(new_text);
10831                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10832                                   GINT_TO_POINTER(paste_as_quotation - 1));
10833                                   
10834                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10835                 gtk_text_buffer_place_cursor(buffer, iter);
10836                 gtk_text_buffer_delete_mark(buffer, mark);
10837
10838                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10839                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10840                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10841                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10842                 gtk_text_buffer_delete_mark(buffer, mark);
10843         } else {
10844                 if (strcmp(text, "\n") || compose->automatic_break
10845                 || gtk_text_iter_starts_line(iter)) {
10846                         GtkTextIter before_ins;
10847                         gtk_text_buffer_insert(buffer, iter, text, len);
10848                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10849                                 before_ins = *iter; 
10850                                 gtk_text_iter_backward_chars(&before_ins, len);
10851                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10852                         }
10853                 } else {
10854                         /* check if the preceding is just whitespace or quote */
10855                         GtkTextIter start_line;
10856                         gchar *tmp = NULL, *quote = NULL;
10857                         gint quote_len = 0, is_normal = 0;
10858                         start_line = *iter;
10859                         gtk_text_iter_set_line_offset(&start_line, 0); 
10860                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10861                         g_strstrip(tmp);
10862
10863                         if (*tmp == '\0') {
10864                                 is_normal = 1;
10865                         } else {
10866                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10867                                 if (quote)
10868                                         is_normal = 1;
10869                                 g_free(quote);
10870                         }
10871                         g_free(tmp);
10872                         
10873                         if (is_normal) {
10874                                 gtk_text_buffer_insert(buffer, iter, text, len);
10875                         } else {
10876                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10877                                         iter, text, len, "no_join", NULL);
10878                         }
10879                 }
10880         }
10881         
10882         if (!paste_as_quotation) {
10883                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10884                 compose_beautify_paragraph(compose, iter, FALSE);
10885                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10886                 gtk_text_buffer_delete_mark(buffer, mark);
10887         }
10888
10889         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10890                                           G_CALLBACK(text_inserted),
10891                                           compose);
10892         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10893
10894         if (prefs_common.autosave && 
10895             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10896             compose->draft_timeout_tag != -2 /* disabled while loading */)
10897                 compose->draft_timeout_tag = g_timeout_add
10898                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10899 }
10900 static gint compose_defer_auto_save_draft(Compose *compose)
10901 {
10902         compose->draft_timeout_tag = -1;
10903         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10904         return FALSE;
10905 }
10906
10907 #if USE_ENCHANT
10908 static void compose_check_all(GtkAction *action, gpointer data)
10909 {
10910         Compose *compose = (Compose *)data;
10911         if (!compose->gtkaspell)
10912                 return;
10913                 
10914         if (gtkut_widget_has_focus(compose->subject_entry))
10915                 claws_spell_entry_check_all(
10916                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
10917         else
10918                 gtkaspell_check_all(compose->gtkaspell);
10919 }
10920
10921 static void compose_highlight_all(GtkAction *action, gpointer data)
10922 {
10923         Compose *compose = (Compose *)data;
10924         if (compose->gtkaspell) {
10925                 claws_spell_entry_recheck_all(
10926                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10927                 gtkaspell_highlight_all(compose->gtkaspell);
10928         }
10929 }
10930
10931 static void compose_check_backwards(GtkAction *action, gpointer data)
10932 {
10933         Compose *compose = (Compose *)data;
10934         if (!compose->gtkaspell) {
10935                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10936                 return;
10937         }
10938
10939         if (gtkut_widget_has_focus(compose->subject_entry))
10940                 claws_spell_entry_check_backwards(
10941                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10942         else
10943                 gtkaspell_check_backwards(compose->gtkaspell);
10944 }
10945
10946 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10947 {
10948         Compose *compose = (Compose *)data;
10949         if (!compose->gtkaspell) {
10950                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10951                 return;
10952         }
10953
10954         if (gtkut_widget_has_focus(compose->subject_entry))
10955                 claws_spell_entry_check_forwards_go(
10956                         CLAWS_SPELL_ENTRY(compose->subject_entry));
10957         else
10958                 gtkaspell_check_forwards_go(compose->gtkaspell);
10959 }
10960 #endif
10961
10962 /*!
10963  *\brief        Guess originating forward account from MsgInfo and several 
10964  *              "common preference" settings. Return NULL if no guess. 
10965  */
10966 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10967 {
10968         PrefsAccount *account = NULL;
10969         
10970         cm_return_val_if_fail(msginfo, NULL);
10971         cm_return_val_if_fail(msginfo->folder, NULL);
10972         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10973
10974         if (msginfo->folder->prefs->enable_default_account)
10975                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10976                 
10977         if (!account) 
10978                 account = msginfo->folder->folder->account;
10979                 
10980         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10981                 gchar *to;
10982                 Xstrdup_a(to, msginfo->to, return NULL);
10983                 extract_address(to);
10984                 account = account_find_from_address(to, FALSE);
10985         }
10986
10987         if (!account && prefs_common.forward_account_autosel) {
10988                 gchar cc[BUFFSIZE];
10989                 if (!procheader_get_header_from_msginfo
10990                         (msginfo, cc,sizeof cc , "Cc:")) { 
10991                         gchar *buf = cc + strlen("Cc:");
10992                         extract_address(buf);
10993                         account = account_find_from_address(buf, FALSE);
10994                 }
10995         }
10996         
10997         if (!account && prefs_common.forward_account_autosel) {
10998                 gchar deliveredto[BUFFSIZE];
10999                 if (!procheader_get_header_from_msginfo
11000                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11001                         gchar *buf = deliveredto + strlen("Delivered-To:");
11002                         extract_address(buf);
11003                         account = account_find_from_address(buf, FALSE);
11004                 }
11005         }
11006         
11007         return account;
11008 }
11009
11010 gboolean compose_close(Compose *compose)
11011 {
11012         gint x, y;
11013
11014         if (!g_mutex_trylock(compose->mutex)) {
11015                 /* we have to wait for the (possibly deferred by auto-save)
11016                  * drafting to be done, before destroying the compose under
11017                  * it. */
11018                 debug_print("waiting for drafting to finish...\n");
11019                 compose_allow_user_actions(compose, FALSE);
11020                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11021                 return FALSE;
11022         }
11023         cm_return_val_if_fail(compose, FALSE);
11024         gtkut_widget_get_uposition(compose->window, &x, &y);
11025         if (!compose->batch) {
11026                 prefs_common.compose_x = x;
11027                 prefs_common.compose_y = y;
11028         }
11029         g_mutex_unlock(compose->mutex);
11030         compose_destroy(compose);
11031         return FALSE;
11032 }
11033
11034 /**
11035  * Add entry field for each address in list.
11036  * \param compose     E-Mail composition object.
11037  * \param listAddress List of (formatted) E-Mail addresses.
11038  */
11039 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11040         GList *node;
11041         gchar *addr;
11042         node = listAddress;
11043         while( node ) {
11044                 addr = ( gchar * ) node->data;
11045                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11046                 node = g_list_next( node );
11047         }
11048 }
11049
11050 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11051                                     guint action, gboolean opening_multiple)
11052 {
11053         gchar *body = NULL;
11054         GSList *new_msglist = NULL;
11055         MsgInfo *tmp_msginfo = NULL;
11056         gboolean originally_enc = FALSE;
11057         gboolean originally_sig = FALSE;
11058         Compose *compose = NULL;
11059         gchar *s_system = NULL;
11060
11061         cm_return_if_fail(msgview != NULL);
11062
11063         cm_return_if_fail(msginfo_list != NULL);
11064
11065         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11066                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11067                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11068
11069                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11070                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11071                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11072                                                 orig_msginfo, mimeinfo);
11073                         if (tmp_msginfo != NULL) {
11074                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11075
11076                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11077                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11078                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11079
11080                                 tmp_msginfo->folder = orig_msginfo->folder;
11081                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11082                                 if (orig_msginfo->tags) {
11083                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11084                                         tmp_msginfo->folder->tags_dirty = TRUE;
11085                                 }
11086                         }
11087                 }
11088         }
11089
11090         if (!opening_multiple)
11091                 body = messageview_get_selection(msgview);
11092
11093         if (new_msglist) {
11094                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11095                 procmsg_msginfo_free(tmp_msginfo);
11096                 g_slist_free(new_msglist);
11097         } else
11098                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11099
11100         if (compose && originally_enc) {
11101                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11102         }
11103
11104         if (compose && originally_sig && compose->account->default_sign_reply) {
11105                 compose_force_signing(compose, compose->account, s_system);
11106         }
11107         g_free(s_system);
11108         g_free(body);
11109         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11110 }
11111
11112 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11113                                     guint action)
11114 {
11115         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11116         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11117                 GSList *cur = msginfo_list;
11118                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11119                                                "messages. Opening the windows "
11120                                                "could take some time. Do you "
11121                                                "want to continue?"), 
11122                                                g_slist_length(msginfo_list));
11123                 if (g_slist_length(msginfo_list) > 9
11124                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11125                     != G_ALERTALTERNATE) {
11126                         g_free(msg);
11127                         return;
11128                 }
11129                 g_free(msg);
11130                 /* We'll open multiple compose windows */
11131                 /* let the WM place the next windows */
11132                 compose_force_window_origin = FALSE;
11133                 for (; cur; cur = cur->next) {
11134                         GSList tmplist;
11135                         tmplist.data = cur->data;
11136                         tmplist.next = NULL;
11137                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11138                 }
11139                 compose_force_window_origin = TRUE;
11140         } else {
11141                 /* forwarding multiple mails as attachments is done via a
11142                  * single compose window */
11143                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11144         }
11145 }
11146
11147 void compose_check_for_email_account(Compose *compose)
11148 {
11149         PrefsAccount *ac = NULL, *curr = NULL;
11150         GList *list;
11151         
11152         if (!compose)
11153                 return;
11154
11155         if (compose->account && compose->account->protocol == A_NNTP) {
11156                 ac = account_get_cur_account();
11157                 if (ac->protocol == A_NNTP) {
11158                         list = account_get_list();
11159                         
11160                         for( ; list != NULL ; list = g_list_next(list)) {
11161                                 curr = (PrefsAccount *) list->data;
11162                                 if (curr->protocol != A_NNTP) {
11163                                         ac = curr;
11164                                         break;
11165                                 }
11166                         }
11167                 }
11168                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11169                                         ac->account_id); 
11170         }
11171 }
11172
11173 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
11174                                 const gchar *address)
11175 {
11176         GSList *msginfo_list = NULL;
11177         gchar *body =  messageview_get_selection(msgview);
11178         Compose *compose;
11179         
11180         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11181         
11182         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11183         compose_check_for_email_account(compose);
11184         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11185         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11186         compose_reply_set_subject(compose, msginfo);
11187
11188         g_free(body);
11189         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11190 }
11191
11192 void compose_set_position(Compose *compose, gint pos)
11193 {
11194         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11195
11196         gtkut_text_view_set_position(text, pos);
11197 }
11198
11199 gboolean compose_search_string(Compose *compose,
11200                                 const gchar *str, gboolean case_sens)
11201 {
11202         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11203
11204         return gtkut_text_view_search_string(text, str, case_sens);
11205 }
11206
11207 gboolean compose_search_string_backward(Compose *compose,
11208                                 const gchar *str, gboolean case_sens)
11209 {
11210         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11211
11212         return gtkut_text_view_search_string_backward(text, str, case_sens);
11213 }
11214
11215 /* allocate a msginfo structure and populate its data from a compose data structure */
11216 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11217 {
11218         MsgInfo *newmsginfo;
11219         GSList *list;
11220         gchar buf[BUFFSIZE];
11221
11222         cm_return_val_if_fail( compose != NULL, NULL );
11223
11224         newmsginfo = procmsg_msginfo_new();
11225
11226         /* date is now */
11227         get_rfc822_date(buf, sizeof(buf));
11228         newmsginfo->date = g_strdup(buf);
11229
11230         /* from */
11231         if (compose->from_name) {
11232                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11233                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11234         }
11235
11236         /* subject */
11237         if (compose->subject_entry)
11238                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11239
11240         /* to, cc, reply-to, newsgroups */
11241         for (list = compose->header_list; list; list = list->next) {
11242                 gchar *header = gtk_editable_get_chars(
11243                                                                 GTK_EDITABLE(
11244                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11245                 gchar *entry = gtk_editable_get_chars(
11246                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11247
11248                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11249                         if ( newmsginfo->to == NULL ) {
11250                                 newmsginfo->to = g_strdup(entry);
11251                         } else if (entry && *entry) {
11252                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11253                                 g_free(newmsginfo->to);
11254                                 newmsginfo->to = tmp;
11255                         }
11256                 } else
11257                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11258                         if ( newmsginfo->cc == NULL ) {
11259                                 newmsginfo->cc = g_strdup(entry);
11260                         } else if (entry && *entry) {
11261                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11262                                 g_free(newmsginfo->cc);
11263                                 newmsginfo->cc = tmp;
11264                         }
11265                 } else
11266                 if ( strcasecmp(header,
11267                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11268                         if ( newmsginfo->newsgroups == NULL ) {
11269                                 newmsginfo->newsgroups = g_strdup(entry);
11270                         } else if (entry && *entry) {
11271                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11272                                 g_free(newmsginfo->newsgroups);
11273                                 newmsginfo->newsgroups = tmp;
11274                         }
11275                 }
11276
11277                 g_free(header);
11278                 g_free(entry);  
11279         }
11280
11281         /* other data is unset */
11282
11283         return newmsginfo;
11284 }
11285
11286 #ifdef USE_ENCHANT
11287 /* update compose's dictionaries from folder dict settings */
11288 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11289                                                 FolderItem *folder_item)
11290 {
11291         cm_return_if_fail(compose != NULL);
11292
11293         if (compose->gtkaspell && folder_item && folder_item->prefs) {
11294                 FolderItemPrefs *prefs = folder_item->prefs;
11295
11296                 if (prefs->enable_default_dictionary)
11297                         gtkaspell_change_dict(compose->gtkaspell,
11298                                         prefs->default_dictionary, FALSE);
11299                 if (folder_item->prefs->enable_default_alt_dictionary)
11300                         gtkaspell_change_alt_dict(compose->gtkaspell,
11301                                         prefs->default_alt_dictionary);
11302                 if (prefs->enable_default_dictionary
11303                         || prefs->enable_default_alt_dictionary)
11304                         compose_spell_menu_changed(compose);
11305         }
11306 }
11307 #endif
11308
11309 /*
11310  * End of Source.
11311  */