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