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