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