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