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