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