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