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