2005-10-06 [colin] 1.9.15cvs17
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkvpaned.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkeditable.h>
43 #include <gtk/gtkwindow.h>
44 #include <gtk/gtksignal.h>
45 #include <gtk/gtkvbox.h>
46 #include <gtk/gtkcontainer.h>
47 #include <gtk/gtkhandlebox.h>
48 #include <gtk/gtktoolbar.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtklabel.h>
52 #include <gtk/gtkscrolledwindow.h>
53 #include <gtk/gtktreeview.h>
54 #include <gtk/gtkliststore.h>
55 #include <gtk/gtktreeselection.h>
56 #include <gtk/gtktreemodel.h>
57
58 #include <gtk/gtkdnd.h>
59 #include <gtk/gtkclipboard.h>
60 #include <pango/pango-break.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <ctype.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <unistd.h>
68 #include <time.h>
69 #include <stdlib.h>
70 #if HAVE_SYS_WAIT_H
71 #  include <sys/wait.h>
72 #endif
73 #include <signal.h>
74 #include <errno.h>
75 #include <libgen.h>
76
77 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
78 #  include <wchar.h>
79 #  include <wctype.h>
80 #endif
81
82 #include "sylpheed.h"
83 #include "main.h"
84 #include "mainwindow.h"
85 #include "compose.h"
86 #include "addressbook.h"
87 #include "folderview.h"
88 #include "procmsg.h"
89 #include "menu.h"
90 #include "stock_pixmap.h"
91 #include "send_message.h"
92 #include "imap.h"
93 #include "news.h"
94 #include "customheader.h"
95 #include "prefs_common.h"
96 #include "prefs_account.h"
97 #include "action.h"
98 #include "account.h"
99 #include "filesel.h"
100 #include "procheader.h"
101 #include "procmime.h"
102 #include "statusbar.h"
103 #include "about.h"
104 #include "base64.h"
105 #include "quoted-printable.h"
106 #include "codeconv.h"
107 #include "utils.h"
108 #include "gtkutils.h"
109 #include "socket.h"
110 #include "alertpanel.h"
111 #include "manage_window.h"
112 #include "gtkshruler.h"
113 #include "folder.h"
114 #include "addr_compl.h"
115 #include "quote_fmt.h"
116 #include "template.h"
117 #include "undo.h"
118 #include "foldersel.h"
119 #include "toolbar.h"
120 #include "inc.h"
121 enum
122 {
123         COL_MIMETYPE = 0,
124         COL_SIZE     = 1,
125         COL_NAME     = 2,
126         COL_DATA     = 3,
127         COL_AUTODATA = 4,
128         N_COL_COLUMNS
129 };
130
131 #define N_ATTACH_COLS   (N_COL_COLUMNS)
132
133 typedef enum
134 {
135         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
136         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
137         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
138         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
139         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
140         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
141         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
142         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
143         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
144         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
145         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
146         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
147         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
148         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
149         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
150 } ComposeCallAdvancedAction;
151
152 typedef enum
153 {
154         PRIORITY_HIGHEST = 1,
155         PRIORITY_HIGH,
156         PRIORITY_NORMAL,
157         PRIORITY_LOW,
158         PRIORITY_LOWEST
159 } PriorityLevel;
160
161 typedef enum
162 {
163         COMPOSE_INSERT_SUCCESS,
164         COMPOSE_INSERT_READ_ERROR,
165         COMPOSE_INSERT_INVALID_CHARACTER,
166         COMPOSE_INSERT_NO_FILE
167 } ComposeInsertResult;
168
169 typedef enum
170 {
171         COMPOSE_QUIT_EDITING,
172         COMPOSE_KEEP_EDITING,
173         COMPOSE_AUTO_SAVE
174 } ComposeDraftAction;
175
176 typedef enum
177 {
178         COMPOSE_WRITE_FOR_SEND,
179         COMPOSE_WRITE_FOR_STORE
180 } ComposeWriteType;
181
182 #define B64_LINE_SIZE           57
183 #define B64_BUFFSIZE            77
184
185 #define MAX_REFERENCES_LEN      999
186
187 static GList *compose_list = NULL;
188
189 Compose *compose_generic_new                    (PrefsAccount   *account,
190                                                  const gchar    *to,
191                                                  FolderItem     *item,
192                                                  GPtrArray      *attach_files,
193                                                  GList          *listAddress );
194
195 static Compose *compose_create                  (PrefsAccount   *account,
196                                                  ComposeMode     mode);
197
198 static GtkWidget *compose_account_option_menu_create
199                                                 (Compose        *compose);
200 static void compose_set_out_encoding            (Compose        *compose);
201 static void compose_set_template_menu           (Compose        *compose);
202 static void compose_template_apply              (Compose        *compose,
203                                                  Template       *tmpl,
204                                                  gboolean        replace);
205 static void compose_destroy                     (Compose        *compose);
206
207 static void compose_entries_set                 (Compose        *compose,
208                                                  const gchar    *mailto);
209 static gint compose_parse_header                (Compose        *compose,
210                                                  MsgInfo        *msginfo);
211 static gchar *compose_parse_references          (const gchar    *ref,
212                                                  const gchar    *msgid);
213
214 static gchar *compose_quote_fmt                 (Compose        *compose,
215                                                  MsgInfo        *msginfo,
216                                                  const gchar    *fmt,
217                                                  const gchar    *qmark,
218                                                  const gchar    *body);
219
220 static void compose_reply_set_entry             (Compose        *compose,
221                                                  MsgInfo        *msginfo,
222                                                  gboolean        to_all,
223                                                  gboolean        to_ml,
224                                                  gboolean        to_sender,
225                                                  gboolean
226                                                  followup_and_reply_to);
227 static void compose_reedit_set_entry            (Compose        *compose,
228                                                  MsgInfo        *msginfo);
229
230 static void compose_insert_sig                  (Compose        *compose,
231                                                  gboolean        replace);
232 static gchar *compose_get_signature_str         (Compose        *compose);
233 static ComposeInsertResult compose_insert_file  (Compose        *compose,
234                                                  const gchar    *file);
235
236 static void compose_attach_append               (Compose        *compose,
237                                                  const gchar    *file,
238                                                  const gchar    *type,
239                                                  const gchar    *content_type);
240 static void compose_attach_parts                (Compose        *compose,
241                                                  MsgInfo        *msginfo);
242
243 static void compose_beautify_paragraph          (Compose        *compose,
244                                                  GtkTextIter    *par_iter,
245                                                  gboolean        force);
246 static void compose_wrap_all                    (Compose        *compose);
247 static void compose_wrap_all_full               (Compose        *compose,
248                                                  gboolean        autowrap);
249
250 static void compose_set_title                   (Compose        *compose);
251 static void compose_select_account              (Compose        *compose,
252                                                  PrefsAccount   *account,
253                                                  gboolean        init);
254
255 static PrefsAccount *compose_current_mail_account(void);
256 /* static gint compose_send                     (Compose        *compose); */
257 static gboolean compose_check_for_valid_recipient
258                                                 (Compose        *compose);
259 static gboolean compose_check_entries           (Compose        *compose,
260                                                  gboolean       check_subject);
261 static gint compose_write_to_file               (Compose        *compose,
262                                                  FILE           *fp,
263                                                  gint            action);
264 static gint compose_write_body_to_file          (Compose        *compose,
265                                                  const gchar    *file);
266 static gint compose_remove_reedit_target        (Compose        *compose);
267 void compose_remove_draft                       (Compose        *compose);
268 static gint compose_queue                       (Compose        *compose,
269                                                  gint           *msgnum,
270                                                  FolderItem     **item);
271 static gint compose_queue_sub                   (Compose        *compose,
272                                                  gint           *msgnum,
273                                                  FolderItem     **item,
274                                                  gboolean       check_subject);
275 static void compose_add_attachments             (Compose        *compose,
276                                                  MimeInfo       *parent);
277 static gchar *compose_get_header                (Compose        *compose);
278
279 static void compose_convert_header              (Compose        *compose,
280                                                  gchar          *dest,
281                                                  gint            len,
282                                                  gchar          *src,
283                                                  gint            header_len,
284                                                  gboolean        addr_field);
285
286 static void compose_attach_info_free            (AttachInfo     *ainfo);
287 static void compose_attach_remove_selected      (Compose        *compose);
288
289 static void compose_attach_property             (Compose        *compose);
290 static void compose_attach_property_create      (gboolean       *cancelled);
291 static void attach_property_ok                  (GtkWidget      *widget,
292                                                  gboolean       *cancelled);
293 static void attach_property_cancel              (GtkWidget      *widget,
294                                                  gboolean       *cancelled);
295 static gint attach_property_delete_event        (GtkWidget      *widget,
296                                                  GdkEventAny    *event,
297                                                  gboolean       *cancelled);
298 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
299                                                  GdkEventKey    *event,
300                                                  gboolean       *cancelled);
301
302 static void compose_exec_ext_editor             (Compose        *compose);
303 #ifdef G_OS_UNIX
304 static gint compose_exec_ext_editor_real        (const gchar    *file);
305 static gboolean compose_ext_editor_kill         (Compose        *compose);
306 static gboolean compose_input_cb                (GIOChannel     *source,
307                                                  GIOCondition    condition,
308                                                  gpointer        data);
309 static void compose_set_ext_editor_sensitive    (Compose        *compose,
310                                                  gboolean        sensitive);
311 #endif /* G_OS_UNIX */
312
313 static void compose_undo_state_changed          (UndoMain       *undostruct,
314                                                  gint            undo_state,
315                                                  gint            redo_state,
316                                                  gpointer        data);
317
318 static void compose_create_header_entry (Compose *compose);
319 static void compose_add_header_entry    (Compose *compose, gchar *header, gchar *text);
320 static void compose_update_priority_menu_item(Compose * compose);
321
322 static void compose_add_field_list      ( Compose *compose,
323                                           GList *listAddress );
324
325 /* callback functions */
326
327 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
328                                          GtkAllocation  *allocation,
329                                          GtkSHRuler     *shruler);
330 static void account_activated           (GtkMenuItem    *menuitem,
331                                          gpointer        data);
332 static void attach_selected             (GtkTreeView    *tree_view, 
333                                          GtkTreePath    *tree_path,
334                                          GtkTreeViewColumn *column, 
335                                          Compose *compose);
336 static gboolean attach_button_pressed   (GtkWidget      *widget,
337                                          GdkEventButton *event,
338                                          gpointer        data);
339 static gboolean attach_key_pressed      (GtkWidget      *widget,
340                                          GdkEventKey    *event,
341                                          gpointer        data);
342
343 static void compose_send_cb             (gpointer        data,
344                                          guint           action,
345                                          GtkWidget      *widget);
346 static void compose_send_later_cb       (gpointer        data,
347                                          guint           action,
348                                          GtkWidget      *widget);
349
350 static void compose_draft_cb            (gpointer        data,
351                                          guint           action,
352                                          GtkWidget      *widget);
353
354 static void compose_attach_cb           (gpointer        data,
355                                          guint           action,
356                                          GtkWidget      *widget);
357 static void compose_insert_file_cb      (gpointer        data,
358                                          guint           action,
359                                          GtkWidget      *widget);
360 static void compose_insert_sig_cb       (gpointer        data,
361                                          guint           action,
362                                          GtkWidget      *widget);
363
364 static void compose_close_cb            (gpointer        data,
365                                          guint           action,
366                                          GtkWidget      *widget);
367
368 static void compose_set_encoding_cb     (gpointer        data,
369                                          guint           action,
370                                          GtkWidget      *widget);
371
372 static void compose_address_cb          (gpointer        data,
373                                          guint           action,
374                                          GtkWidget      *widget);
375 static void compose_template_activate_cb(GtkWidget      *widget,
376                                          gpointer        data);
377
378 static void compose_ext_editor_cb       (gpointer        data,
379                                          guint           action,
380                                          GtkWidget      *widget);
381
382 static gint compose_delete_cb           (GtkWidget      *widget,
383                                          GdkEventAny    *event,
384                                          gpointer        data);
385
386 static void compose_undo_cb             (Compose        *compose);
387 static void compose_redo_cb             (Compose        *compose);
388 static void compose_cut_cb              (Compose        *compose);
389 static void compose_copy_cb             (Compose        *compose);
390 static void compose_paste_cb            (Compose        *compose);
391 static void compose_paste_as_quote_cb   (Compose        *compose);
392 static void compose_paste_no_wrap_cb    (Compose        *compose);
393 static void compose_paste_wrap_cb       (Compose        *compose);
394 static void compose_allsel_cb           (Compose        *compose);
395
396 static void compose_advanced_action_cb  (Compose                   *compose,
397                                          ComposeCallAdvancedAction  action);
398
399 static void compose_grab_focus_cb       (GtkWidget      *widget,
400                                          Compose        *compose);
401
402 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
403                                          Compose        *compose);
404
405 static void compose_wrap_cb             (gpointer        data,
406                                          guint           action,
407                                          GtkWidget      *widget);
408 static void compose_toggle_autowrap_cb  (gpointer        data,
409                                          guint           action,
410                                          GtkWidget      *widget);
411
412 static void compose_toggle_ruler_cb     (gpointer        data,
413                                          guint           action,
414                                          GtkWidget      *widget);
415 static void compose_toggle_sign_cb      (gpointer        data,
416                                          guint           action,
417                                          GtkWidget      *widget);
418 static void compose_toggle_encrypt_cb   (gpointer        data,
419                                          guint           action,
420                                          GtkWidget      *widget);
421 static void compose_set_privacy_system_cb(GtkWidget      *widget,
422                                           gpointer        data);
423 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
424 static void activate_privacy_system     (Compose *compose, 
425                                          PrefsAccount *account,
426                                          gboolean warn);
427 static void compose_use_signing(Compose *compose, gboolean use_signing);
428 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
429 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
430                                              GtkWidget *widget);
431 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
432                                              GtkWidget *widget);
433 static void compose_set_priority_cb     (gpointer        data,
434                                          guint           action,
435                                          GtkWidget      *widget);
436
437 static void compose_attach_drag_received_cb (GtkWidget          *widget,
438                                              GdkDragContext     *drag_context,
439                                              gint                x,
440                                              gint                y,
441                                              GtkSelectionData   *data,
442                                              guint               info,
443                                              guint               time,
444                                              gpointer            user_data);
445 static void compose_insert_drag_received_cb (GtkWidget          *widget,
446                                              GdkDragContext     *drag_context,
447                                              gint                x,
448                                              gint                y,
449                                              GtkSelectionData   *data,
450                                              guint               info,
451                                              guint               time,
452                                              gpointer            user_data);
453 static void compose_header_drag_received_cb (GtkWidget          *widget,
454                                              GdkDragContext     *drag_context,
455                                              gint                x,
456                                              gint                y,
457                                              GtkSelectionData   *data,
458                                              guint               info,
459                                              guint               time,
460                                              gpointer            user_data);
461
462 static gboolean compose_drag_drop           (GtkWidget *widget,
463                                              GdkDragContext *drag_context,
464                                              gint x, gint y,
465                                              guint time, gpointer user_data);
466
467 static void text_inserted               (GtkTextBuffer  *buffer,
468                                          GtkTextIter    *iter,
469                                          const gchar    *text,
470                                          gint            len,
471                                          Compose        *compose);
472 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
473                                   gboolean to_all, gboolean to_ml,
474                                   gboolean to_sender,
475                                   gboolean followup_and_reply_to,
476                                   const gchar *body);
477
478 gboolean compose_headerentry_changed_cb    (GtkWidget          *entry,
479                                             ComposeHeaderEntry *headerentry);
480 gboolean compose_headerentry_key_press_event_cb(GtkWidget              *entry,
481                                             GdkEventKey        *event,
482                                             ComposeHeaderEntry *headerentry);
483
484 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
485
486 static void compose_allow_user_actions (Compose *compose, gboolean allow);
487
488 #if USE_ASPELL
489 static void compose_check_all              (Compose *compose);
490 static void compose_highlight_all          (Compose *compose);
491 static void compose_check_backwards        (Compose *compose);
492 static void compose_check_forwards_go      (Compose *compose);
493 #endif
494
495 static gint compose_defer_auto_save_draft       (Compose        *compose);
496 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
497
498 static gboolean compose_close   (Compose *compose);
499
500 static GtkItemFactoryEntry compose_popup_entries[] =
501 {
502         {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
503         {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
504         {N_("/---"),            NULL, NULL, 0, "<Separator>"},
505         {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
506 };
507
508 static GtkItemFactoryEntry compose_entries[] =
509 {
510         {N_("/_Message"),                               NULL, NULL, 0, "<Branch>"},
511         {N_("/_Message/_Send"),         "<control>Return",
512                                         compose_send_cb, 0, NULL},
513         {N_("/_Message/Send _later"),   "<shift><control>S",
514                                         compose_send_later_cb,  0, NULL},
515         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
516         {N_("/_Message/_Attach file"),          "<control>M", compose_attach_cb,      0, NULL},
517         {N_("/_Message/_Insert file"),          "<control>I", compose_insert_file_cb, 0, NULL},
518         {N_("/_Message/Insert si_gnature"),     "<control>G", compose_insert_sig_cb,  0, NULL},
519         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
520         {N_("/_Message/_Save"),
521                                                 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
522         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
523         {N_("/_Message/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
524
525         {N_("/_Edit"),                  NULL, NULL, 0, "<Branch>"},
526         {N_("/_Edit/_Undo"),            "<control>Z", compose_undo_cb, 0, NULL},
527         {N_("/_Edit/_Redo"),            "<control>Y", compose_redo_cb, 0, NULL},
528         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
529         {N_("/_Edit/Cu_t"),             "<control>X", compose_cut_cb,    0, NULL},
530         {N_("/_Edit/_Copy"),            "<control>C", compose_copy_cb,   0, NULL},
531         {N_("/_Edit/_Paste"),           "<control>V", compose_paste_cb,  0, NULL},
532         {N_("/_Edit/Special paste/as _quotation"),
533                                         NULL, compose_paste_as_quote_cb, 0, NULL},
534         {N_("/_Edit/Special paste/_wrapped"),
535                                         NULL, compose_paste_wrap_cb, 0, NULL},
536         {N_("/_Edit/Special paste/_unwrapped"),
537                                         NULL, compose_paste_no_wrap_cb, 0, NULL},
538         {N_("/_Edit/Select _all"),      "<control>A", compose_allsel_cb, 0, NULL},
539         {N_("/_Edit/A_dvanced"),        NULL, NULL, 0, "<Branch>"},
540         {N_("/_Edit/A_dvanced/Move a character backward"),
541                                         "<control>B",
542                                         compose_advanced_action_cb,
543                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
544                                         NULL},
545         {N_("/_Edit/A_dvanced/Move a character forward"),
546                                         "<control>F",
547                                         compose_advanced_action_cb,
548                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
549                                         NULL},
550         {N_("/_Edit/A_dvanced/Move a word backward"),
551                                         NULL, /* "<alt>B" */
552                                         compose_advanced_action_cb,
553                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
554                                         NULL},
555         {N_("/_Edit/A_dvanced/Move a word forward"),
556                                         NULL, /* "<alt>F" */
557                                         compose_advanced_action_cb,
558                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
559                                         NULL},
560         {N_("/_Edit/A_dvanced/Move to beginning of line"),
561                                         NULL, /* "<control>A" */
562                                         compose_advanced_action_cb,
563                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
564                                         NULL},
565         {N_("/_Edit/A_dvanced/Move to end of line"),
566                                         "<control>E",
567                                         compose_advanced_action_cb,
568                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
569                                         NULL},
570         {N_("/_Edit/A_dvanced/Move to previous line"),
571                                         "<control>P",
572                                         compose_advanced_action_cb,
573                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
574                                         NULL},
575         {N_("/_Edit/A_dvanced/Move to next line"),
576                                         "<control>N",
577                                         compose_advanced_action_cb,
578                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
579                                         NULL},
580         {N_("/_Edit/A_dvanced/Delete a character backward"),
581                                         "<control>H",
582                                         compose_advanced_action_cb,
583                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
584                                         NULL},
585         {N_("/_Edit/A_dvanced/Delete a character forward"),
586                                         "<control>D",
587                                         compose_advanced_action_cb,
588                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
589                                         NULL},
590         {N_("/_Edit/A_dvanced/Delete a word backward"),
591                                         NULL, /* "<control>W" */
592                                         compose_advanced_action_cb,
593                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
594                                         NULL},
595         {N_("/_Edit/A_dvanced/Delete a word forward"),
596                                         NULL, /* "<alt>D", */
597                                         compose_advanced_action_cb,
598                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
599                                         NULL},
600         {N_("/_Edit/A_dvanced/Delete line"),
601                                         "<control>U",
602                                         compose_advanced_action_cb,
603                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
604                                         NULL},
605         {N_("/_Edit/A_dvanced/Delete entire line"),
606                                         NULL,
607                                         compose_advanced_action_cb,
608                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
609                                         NULL},
610         {N_("/_Edit/A_dvanced/Delete to end of line"),
611                                         "<control>K",
612                                         compose_advanced_action_cb,
613                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
614                                         NULL},
615         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
616         {N_("/_Edit/_Wrap current paragraph"),
617                                         "<control>L", compose_wrap_cb, 0, NULL},
618         {N_("/_Edit/Wrap all long _lines"),
619                                         "<control><alt>L", compose_wrap_cb, 1, NULL},
620         {N_("/_Edit/Aut_o wrapping"),   "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
621         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
622         {N_("/_Edit/Edit with e_xternal editor"),
623                                         "<shift><control>X", compose_ext_editor_cb, 0, NULL},
624 #if USE_ASPELL
625         {N_("/_Spelling"),              NULL, NULL, 0, "<Branch>"},
626         {N_("/_Spelling/_Check all or check selection"),
627                                         NULL, compose_check_all, 0, NULL},
628         {N_("/_Spelling/_Highlight all misspelled words"),
629                                         NULL, compose_highlight_all, 0, NULL},
630         {N_("/_Spelling/Check _backwards misspelled word"),
631                                         NULL, compose_check_backwards , 0, NULL},
632         {N_("/_Spelling/_Forward to next misspelled word"),
633                                         NULL, compose_check_forwards_go, 0, NULL},
634         {N_("/_Spelling/---"),          NULL, NULL, 0, "<Separator>"},
635         {N_("/_Spelling/_Spelling Configuration"),
636                                         NULL, NULL, 0, "<Branch>"},
637 #endif
638         {N_("/_Options"),               NULL, NULL, 0, "<Branch>"},
639         {N_("/_Options/Privacy System"),                NULL, NULL,   0, "<Branch>"},
640         {N_("/_Options/Privacy System/None"),   NULL, NULL,   0, "<RadioItem>"},
641         {N_("/_Options/Si_gn"),         NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
642         {N_("/_Options/_Encrypt"),      NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
643         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
644         {N_("/_Options/_Priority"),     NULL,           NULL,   0, "<Branch>"},
645         {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
646         {N_("/_Options/Priority/Hi_gh"),    NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
647         {N_("/_Options/Priority/_Normal"),  NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
648         {N_("/_Options/Priority/Lo_w"),    NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
649         {N_("/_Options/Priority/_Lowest"),  NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
650         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
651         {N_("/_Options/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
652         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
653         {N_("/_Options/Remo_ve references"),    NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
654         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
655
656 #define ENC_ACTION(action) \
657         NULL, compose_set_encoding_cb, action, \
658         "/Options/Character encoding/Automatic"
659
660         {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
661         {N_("/_Options/Character _encoding/_Automatic"),
662                         NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
663         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
664
665         {N_("/_Options/Character _encoding/7bit ascii (US-ASC_II)"),
666          ENC_ACTION(C_US_ASCII)},
667         {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
668          ENC_ACTION(C_UTF_8)},
669         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
670
671         {N_("/_Options/Character _encoding/Western European (ISO-8859-_1)"),
672          ENC_ACTION(C_ISO_8859_1)},
673         {N_("/_Options/Character _encoding/Western European (ISO-8859-15)"),
674          ENC_ACTION(C_ISO_8859_15)},
675         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
676
677         {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
678          ENC_ACTION(C_ISO_8859_2)},
679         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
680
681         {N_("/_Options/Character _encoding/_Baltic (ISO-8859-13)"),
682          ENC_ACTION(C_ISO_8859_13)},
683         {N_("/_Options/Character _encoding/Baltic (ISO-8859-_4)"),
684          ENC_ACTION(C_ISO_8859_4)},
685         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
686
687         {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
688          ENC_ACTION(C_ISO_8859_7)},
689         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
690
691         {N_("/_Options/Character _encoding/Hebrew (ISO-8859-_8)"),
692          ENC_ACTION(C_ISO_8859_8)},
693         {N_("/_Options/Character _encoding/Hebrew (Windows-1255)"),
694          ENC_ACTION(C_WINDOWS_1255)},
695         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
696
697         {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
698          ENC_ACTION(C_ISO_8859_9)},
699         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
700
701         {N_("/_Options/Character _encoding/Cyrillic (ISO-8859-_5)"),
702          ENC_ACTION(C_ISO_8859_5)},
703         {N_("/_Options/Character _encoding/Cyrillic (KOI8-_R)"),
704          ENC_ACTION(C_KOI8_R)},
705         {N_("/_Options/Character _encoding/Cyrillic (KOI8-U)"),
706          ENC_ACTION(C_KOI8_U)},
707         {N_("/_Options/Character _encoding/Cyrillic (Windows-1251)"),
708          ENC_ACTION(C_WINDOWS_1251)},
709         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
710
711         {N_("/_Options/Character _encoding/Japanese (ISO-2022-_JP)"),
712          ENC_ACTION(C_ISO_2022_JP)},
713         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
714
715         {N_("/_Options/Character _encoding/Simplified Chinese (_GB2312)"),
716          ENC_ACTION(C_GB2312)},
717         {N_("/_Options/Character _encoding/Simplified Chinese (GBK)"),
718          ENC_ACTION(C_GBK)},
719         {N_("/_Options/Character _encoding/Traditional Chinese (_Big5)"),
720          ENC_ACTION(C_BIG5)},
721         {N_("/_Options/Character _encoding/Traditional Chinese (EUC-_TW)"),
722          ENC_ACTION(C_EUC_TW)},
723         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
724
725         {N_("/_Options/Character _encoding/Korean (EUC-_KR)"),
726          ENC_ACTION(C_EUC_KR)},
727         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
728
729         {N_("/_Options/Character _encoding/Thai (TIS-620)"),
730          ENC_ACTION(C_TIS_620)},
731         {N_("/_Options/Character _encoding/Thai (Windows-874)"),
732          ENC_ACTION(C_WINDOWS_874)},
733
734         {N_("/_Tools"),                 NULL, NULL, 0, "<Branch>"},
735         {N_("/_Tools/Show _ruler"),     NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
736         {N_("/_Tools/_Address book"),   "<shift><control>A", compose_address_cb , 0, NULL},
737         {N_("/_Tools/_Template"),       NULL, NULL, 0, "<Branch>"},
738         {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
739         {N_("/_Help"),                  NULL, NULL, 0, "<Branch>"},
740         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
741 };
742
743 static GtkTargetEntry compose_mime_types[] =
744 {
745         {"text/uri-list", 0, 0},
746         {"text/plain", 0, 0},
747         {"STRING", 0, 0}
748 };
749
750 static gboolean compose_put_existing_to_front(MsgInfo *info)
751 {
752         GList *compose_list = compose_get_compose_list();
753         GList *elem = NULL;
754         
755         if (compose_list) {
756                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
757                      elem = elem->next) {
758                         Compose *c = (Compose*)elem->data;
759
760                         if (!c->targetinfo || !c->targetinfo->msgid ||
761                             !info->msgid)
762                                 continue;
763
764                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
765                                 gtkut_window_popup(c->window);
766                                 return TRUE;
767                         }
768                 }
769         }
770         return FALSE;
771 }
772
773 static GdkColor quote_color = 
774         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775
776 static GdkColor signature_color = {
777         (gulong)0,
778         (gushort)0x7fff,
779         (gushort)0x7fff,
780         (gushort)0x7fff
781 };
782
783 static GdkColor uri_color = {
784         (gulong)0,
785         (gushort)0,
786         (gushort)0,
787         (gushort)0
788 };
789
790 static void compose_create_tags(GtkTextView *text, Compose *compose)
791 {
792         GtkTextBuffer *buffer;
793         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
794
795         buffer = gtk_text_view_get_buffer(text);
796         
797         if (prefs_common.enable_color) {
798                 /* grab the quote colors, converting from an int to a GdkColor */
799                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
800                                                &quote_color);
801                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
802                                                &signature_color);
803                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
804                                                &uri_color);
805         } else {
806                 signature_color = quote_color = uri_color = black;
807         }
808
809         gtk_text_buffer_create_tag(buffer, "quote",
810                                    "foreground-gdk", &quote_color,
811                                    NULL);
812         gtk_text_buffer_create_tag(buffer, "signature",
813                                    "foreground-gdk", &signature_color,
814                                    NULL);
815         gtk_text_buffer_create_tag(buffer, "link",
816                                          "foreground-gdk", &uri_color,
817                                          NULL);
818         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
819 }
820
821 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
822                      GPtrArray *attach_files)
823 {
824         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
825 }
826
827 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item)
828 {
829         return compose_generic_new(account, NULL, item, NULL, NULL);
830 }
831
832 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
833 {
834         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
835 }
836
837 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
838                              GPtrArray *attach_files, GList *listAddress )
839 {
840         Compose *compose;
841         GtkTextView *textview;
842         GtkTextBuffer *textbuf;
843         GtkTextIter iter;
844         GtkItemFactory *ifactory;
845
846         if (item && item->prefs && item->prefs->enable_default_account)
847                 account = account_find_from_id(item->prefs->default_account);
848
849         if (!account) account = cur_account;
850         g_return_val_if_fail(account != NULL, NULL);
851
852         compose = compose_create(account, COMPOSE_NEW);
853         ifactory = gtk_item_factory_from_widget(compose->menubar);
854
855         compose->replyinfo = NULL;
856         compose->fwdinfo   = NULL;
857
858         textview = GTK_TEXT_VIEW(compose->text);
859         textbuf = gtk_text_view_get_buffer(textview);
860         compose_create_tags(textview, compose);
861
862         undo_block(compose->undostruct);
863 #ifdef USE_ASPELL
864         if (item && item->prefs && item->prefs->enable_default_dictionary &&
865             compose->gtkaspell) 
866                 gtkaspell_change_dict(compose->gtkaspell, 
867                     item->prefs->default_dictionary);
868 #endif
869
870         if (account->auto_sig)
871                 compose_insert_sig(compose, FALSE);
872         gtk_text_buffer_get_start_iter(textbuf, &iter);
873         gtk_text_buffer_place_cursor(textbuf, &iter);
874
875         if (account->protocol != A_NNTP) {
876                 if (mailto && *mailto != '\0') {
877                         compose_entries_set(compose, mailto);
878
879                 } else if (item && item->prefs->enable_default_to) {
880                         compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
881                         compose_entry_mark_default_to(compose, item->prefs->default_to);
882                 }
883                 if (item && item->ret_rcpt) {
884                         menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
885                 }
886         } else {
887                 if (mailto) {
888                         compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
889                 }
890                 /*
891                  * CLAWS: just don't allow return receipt request, even if the user
892                  * may want to send an email. simple but foolproof.
893                  */
894                 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE); 
895         }
896         compose_add_field_list( compose, listAddress );
897
898         if (attach_files) {
899                 gint i;
900                 gchar *file;
901
902                 for (i = 0; i < attach_files->len; i++) {
903                         file = g_ptr_array_index(attach_files, i);
904                         compose_attach_append(compose, file, file, NULL);
905                 }
906         }
907
908         compose_show_first_last_header(compose, TRUE);
909
910         /* Set save folder */
911         if (item && item->prefs && item->prefs->save_copy_to_folder) {
912                 gchar *folderidentifier;
913
914                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
915                 folderidentifier = folder_item_get_identifier(item);
916                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
917                 g_free(folderidentifier);
918         }
919         
920         gtk_widget_grab_focus(compose->header_last->entry);
921
922         undo_unblock(compose->undostruct);
923
924         if (prefs_common.auto_exteditor)
925                 compose_exec_ext_editor(compose);
926
927         compose_set_title(compose);
928         return compose;
929 }
930
931 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
932                 gboolean override_pref)
933 {
934         gchar *privacy = NULL;
935
936         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
937                 return;
938
939         if (account->default_privacy_system
940         &&  strlen(account->default_privacy_system)) {
941                 privacy = account->default_privacy_system;
942         } else {
943                 GSList *privacy_avail = privacy_get_system_ids();
944                 if (privacy_avail && g_slist_length(privacy_avail)) {
945                         privacy = (gchar *)(privacy_avail->data);
946                 }
947         }
948         if (privacy != NULL) {
949                 compose->privacy_system = g_strdup(privacy);
950                 compose_update_privacy_system_menu_item(compose, FALSE);
951                 compose_use_encryption(compose, TRUE);
952         }
953 }       
954
955 static void compose_force_signing(Compose *compose, PrefsAccount *account)
956 {
957         gchar *privacy = NULL;
958
959         if (account->default_privacy_system
960         &&  strlen(account->default_privacy_system)) {
961                 privacy = account->default_privacy_system;
962         } else {
963                 GSList *privacy_avail = privacy_get_system_ids();
964                 if (privacy_avail && g_slist_length(privacy_avail)) {
965                         privacy = (gchar *)(privacy_avail->data);
966                 }
967         }
968         if (privacy != NULL) {
969                 compose->privacy_system = g_strdup(privacy);
970                 compose_update_privacy_system_menu_item(compose, FALSE);
971                 compose_use_signing(compose, TRUE);
972         }
973 }       
974
975 void compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
976 {
977         MsgInfo *msginfo;
978         guint list_len;
979
980         g_return_if_fail(msginfo_list != NULL);
981
982         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
983         g_return_if_fail(msginfo != NULL);
984
985         list_len = g_slist_length(msginfo_list);
986
987         switch (mode) {
988         case COMPOSE_REPLY:
989                 compose_reply(msginfo, prefs_common.reply_with_quote,
990                               FALSE, prefs_common.default_reply_list, FALSE, body);
991                 break;
992         case COMPOSE_REPLY_WITH_QUOTE:
993                 compose_reply(msginfo, TRUE, 
994                         FALSE, prefs_common.default_reply_list, FALSE, body);
995                 break;
996         case COMPOSE_REPLY_WITHOUT_QUOTE:
997                 compose_reply(msginfo, FALSE, 
998                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
999                 break;
1000         case COMPOSE_REPLY_TO_SENDER:
1001                 compose_reply(msginfo, prefs_common.reply_with_quote,
1002                               FALSE, FALSE, TRUE, body);
1003                 break;
1004         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1005                 compose_followup_and_reply_to(msginfo,
1006                                               prefs_common.reply_with_quote,
1007                                               FALSE, FALSE, body);
1008                 break;
1009         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1010                 compose_reply(msginfo, TRUE, 
1011                         FALSE, FALSE, TRUE, body);
1012                 break;
1013         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1014                 compose_reply(msginfo, FALSE, 
1015                         FALSE, FALSE, TRUE, NULL);
1016                 break;
1017         case COMPOSE_REPLY_TO_ALL:
1018                 compose_reply(msginfo, prefs_common.reply_with_quote,
1019                         TRUE, FALSE, FALSE, body);
1020                 break;
1021         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1022                 compose_reply(msginfo, TRUE, 
1023                         TRUE, FALSE, FALSE, body);
1024                 break;
1025         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1026                 compose_reply(msginfo, FALSE, 
1027                         TRUE, FALSE, FALSE, NULL);
1028                 break;
1029         case COMPOSE_REPLY_TO_LIST:
1030                 compose_reply(msginfo, prefs_common.reply_with_quote,
1031                         FALSE, TRUE, FALSE, body);
1032                 break;
1033         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1034                 compose_reply(msginfo, TRUE, 
1035                         FALSE, TRUE, FALSE, body);
1036                 break;
1037         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1038                 compose_reply(msginfo, FALSE, 
1039                         FALSE, TRUE, FALSE, NULL);
1040                 break;
1041         case COMPOSE_FORWARD:
1042                 if (prefs_common.forward_as_attachment) {
1043                         compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1044                         return;
1045                 } else {
1046                         compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1047                         return;
1048                 }
1049                 break;
1050         case COMPOSE_FORWARD_INLINE:
1051                 /* check if we reply to more than one Message */
1052                 if (list_len == 1) {
1053                         compose_forward(NULL, msginfo, FALSE, body, FALSE);
1054                         break;
1055                 } 
1056                 /* more messages FALL THROUGH */
1057         case COMPOSE_FORWARD_AS_ATTACH:
1058                 compose_forward_multiple(NULL, msginfo_list);
1059                 break;
1060         case COMPOSE_REDIRECT:
1061                 compose_redirect(NULL, msginfo);
1062                 break;
1063         default:
1064                 g_warning("compose_reply(): invalid Compose Mode: %d\n", mode);
1065         }
1066 }
1067
1068 void compose_reply(MsgInfo *msginfo, gboolean quote, gboolean to_all,
1069                    gboolean to_ml, gboolean to_sender, 
1070                    const gchar *body)
1071 {
1072         compose_generic_reply(msginfo, quote, to_all, to_ml, 
1073                               to_sender, FALSE, body);
1074 }
1075
1076 void compose_followup_and_reply_to(MsgInfo *msginfo, gboolean quote,
1077                                    gboolean to_all,
1078                                    gboolean to_sender,
1079                                    const gchar *body)
1080 {
1081         compose_generic_reply(msginfo, quote, to_all, FALSE, 
1082                               to_sender, TRUE, body);
1083 }
1084
1085 static void compose_extract_original_charset(Compose *compose)
1086 {
1087         MsgInfo *info = NULL;
1088         if (compose->replyinfo) {
1089                 info = compose->replyinfo;
1090         } else if (compose->fwdinfo) {
1091                 info = compose->fwdinfo;
1092         } else if (compose->targetinfo) {
1093                 info = compose->targetinfo;
1094         }
1095         if (info) {
1096                 MimeInfo *mimeinfo = procmime_scan_message(info);
1097                 MimeInfo *partinfo = mimeinfo;
1098                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1099                         partinfo = procmime_mimeinfo_next(partinfo);
1100                 if (partinfo) {
1101                         compose->orig_charset = 
1102                                 g_strdup(procmime_mimeinfo_get_parameter(
1103                                                 partinfo, "charset"));
1104                 }
1105                 procmime_mimeinfo_free_all(mimeinfo);
1106         }
1107 }
1108
1109 static void compose_generic_reply(MsgInfo *msginfo, gboolean quote,
1110                                   gboolean to_all, gboolean to_ml,
1111                                   gboolean to_sender,
1112                                   gboolean followup_and_reply_to,
1113                                   const gchar *body)
1114 {
1115         GtkItemFactory *ifactory;
1116         Compose *compose;
1117         PrefsAccount *account = NULL;
1118         PrefsAccount *reply_account;
1119         GtkTextView *textview;
1120         GtkTextBuffer *textbuf;
1121         GtkTextIter iter;
1122         int cursor_pos;
1123
1124         g_return_if_fail(msginfo != NULL);
1125         g_return_if_fail(msginfo->folder != NULL);
1126
1127         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1128         
1129         g_return_if_fail(account != NULL);
1130
1131         if (to_sender && account->protocol == A_NNTP &&
1132             !followup_and_reply_to) {
1133                 reply_account =
1134                         account_find_from_address(account->address);
1135                 if (!reply_account)
1136                         reply_account = compose_current_mail_account();
1137                 if (!reply_account)
1138                         return;
1139         } else
1140                 reply_account = account;
1141
1142         compose = compose_create(account, COMPOSE_REPLY);
1143         ifactory = gtk_item_factory_from_widget(compose->menubar);
1144
1145         menu_set_active(ifactory, "/Options/Remove references", FALSE);
1146         menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1147
1148         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1149         if (!compose->replyinfo)
1150                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1151
1152         compose_extract_original_charset(compose);
1153         
1154         if (msginfo->folder && msginfo->folder->ret_rcpt)
1155                 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1156
1157         /* Set save folder */
1158         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1159                 gchar *folderidentifier;
1160
1161                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1162                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1163                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1164                 g_free(folderidentifier);
1165         }
1166
1167         if (compose_parse_header(compose, msginfo) < 0) return;
1168         compose_reply_set_entry(compose, msginfo, to_all, to_ml, 
1169                                 to_sender, followup_and_reply_to);
1170         compose_show_first_last_header(compose, TRUE);
1171
1172         textview = (GTK_TEXT_VIEW(compose->text));
1173         textbuf = gtk_text_view_get_buffer(textview);
1174         compose_create_tags(textview, compose);
1175
1176         undo_block(compose->undostruct);
1177 #ifdef USE_ASPELL
1178         if (msginfo->folder && msginfo->folder->prefs && 
1179             msginfo->folder->prefs && 
1180             msginfo->folder->prefs->enable_default_dictionary &&
1181             compose->gtkaspell)
1182                 gtkaspell_change_dict(compose->gtkaspell, 
1183                     msginfo->folder->prefs->default_dictionary);
1184 #endif
1185
1186         if (quote) {
1187                 gchar *qmark;
1188
1189                 if (prefs_common.quotemark && *prefs_common.quotemark)
1190                         qmark = prefs_common.quotemark;
1191                 else
1192                         qmark = "> ";
1193
1194                 compose_quote_fmt(compose, compose->replyinfo,
1195                                   prefs_common.quotefmt,
1196                                   qmark, body);
1197         }
1198         if (procmime_msginfo_is_encrypted(compose->replyinfo)) {
1199                 compose_force_encryption(compose, account, FALSE);
1200         }
1201
1202         if (account->auto_sig)
1203                 compose_insert_sig(compose, FALSE);
1204
1205         cursor_pos = quote_fmt_get_cursor_pos();
1206         gtk_text_buffer_get_start_iter(textbuf, &iter);
1207         gtk_text_buffer_get_iter_at_offset(textbuf, &iter, cursor_pos);
1208         gtk_text_buffer_place_cursor(textbuf, &iter);
1209         
1210         compose_wrap_all(compose);
1211
1212         gtk_widget_grab_focus(compose->text);
1213
1214         undo_unblock(compose->undostruct);
1215
1216         if (prefs_common.auto_exteditor)
1217                 compose_exec_ext_editor(compose);
1218                 
1219         compose_set_title(compose);
1220 }
1221
1222 #define INSERT_FW_HEADER(var, hdr) \
1223 if (msginfo->var && *msginfo->var) { \
1224         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1225         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1226         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1227 }
1228
1229 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1230                          gboolean as_attach, const gchar *body,
1231                          gboolean no_extedit)
1232 {
1233         Compose *compose;
1234         GtkTextView *textview;
1235         GtkTextBuffer *textbuf;
1236         GtkTextIter iter;
1237
1238         g_return_val_if_fail(msginfo != NULL, NULL);
1239         g_return_val_if_fail(msginfo->folder != NULL, NULL);
1240
1241         if (!account && 
1242             !(account = compose_guess_forward_account_from_msginfo
1243                                 (msginfo)))
1244                 account = cur_account;
1245
1246         compose = compose_create(account, COMPOSE_FORWARD);
1247
1248         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1249         if (!compose->fwdinfo)
1250                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1251
1252         compose_extract_original_charset(compose);
1253
1254         if (msginfo->subject && *msginfo->subject) {
1255                 gchar *buf, *buf2, *p;
1256
1257                 buf = p = g_strdup(msginfo->subject);
1258                 p += subject_get_prefix_length(p);
1259                 memmove(buf, p, strlen(p) + 1);
1260
1261                 buf2 = g_strdup_printf("Fw: %s", buf);
1262                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1263                 
1264                 g_free(buf);
1265                 g_free(buf2);
1266         }
1267
1268         textview = GTK_TEXT_VIEW(compose->text);
1269         textbuf = gtk_text_view_get_buffer(textview);
1270         compose_create_tags(textview, compose);
1271
1272         if (as_attach) {
1273                 gchar *msgfile;
1274
1275                 msgfile = procmsg_get_message_file_path(msginfo);
1276                 if (!is_file_exist(msgfile))
1277                         g_warning("%s: file not exist\n", msgfile);
1278                 else
1279                         compose_attach_append(compose, msgfile, msgfile,
1280                                               "message/rfc822");
1281
1282                 g_free(msgfile);
1283         } else {
1284                 gchar *qmark;
1285                 MsgInfo *full_msginfo;
1286
1287                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1288                 if (!full_msginfo)
1289                         full_msginfo = procmsg_msginfo_copy(msginfo);
1290
1291                 if (prefs_common.fw_quotemark &&
1292                     *prefs_common.fw_quotemark)
1293                         qmark = prefs_common.fw_quotemark;
1294                 else
1295                         qmark = "> ";
1296
1297                 compose_quote_fmt(compose, full_msginfo,
1298                                   prefs_common.fw_quotefmt,
1299                                   qmark, body);
1300                 compose_attach_parts(compose, msginfo);
1301
1302                 procmsg_msginfo_free(full_msginfo);
1303         }
1304
1305         if (account->auto_sig)
1306                 compose_insert_sig(compose, FALSE);
1307
1308         compose_wrap_all(compose);
1309
1310         gtk_text_buffer_get_start_iter(textbuf, &iter);
1311         gtk_text_buffer_place_cursor(textbuf, &iter);
1312
1313         gtk_widget_grab_focus(compose->header_last->entry);
1314
1315         if (!no_extedit && prefs_common.auto_exteditor)
1316                 compose_exec_ext_editor(compose);
1317         
1318         /*save folder*/
1319         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1320                 gchar *folderidentifier;
1321
1322                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1323                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1324                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1325                 g_free(folderidentifier);
1326         }
1327
1328         compose_set_title(compose);
1329         return compose;
1330 }
1331
1332 #undef INSERT_FW_HEADER
1333
1334 Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1335 {
1336         Compose *compose;
1337         GtkTextView *textview;
1338         GtkTextBuffer *textbuf;
1339         GtkTextIter iter;
1340         GSList *msginfo;
1341         gchar *msgfile;
1342
1343         g_return_val_if_fail(msginfo_list != NULL, NULL);
1344
1345         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1346                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1347                         return NULL;
1348
1349         /* guess account from first selected message */
1350         if (!account && 
1351             !(account = compose_guess_forward_account_from_msginfo
1352                                 (msginfo_list->data)))
1353                 account = cur_account;
1354
1355         g_return_val_if_fail(account != NULL, NULL);
1356
1357         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1358                 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1359                 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1360         }
1361
1362         compose = compose_create(account, COMPOSE_FORWARD);
1363
1364         textview = GTK_TEXT_VIEW(compose->text);
1365         textbuf = gtk_text_view_get_buffer(textview);
1366         compose_create_tags(textview, compose);
1367
1368         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1369                 msgfile = procmsg_get_message_file_path((MsgInfo *)msginfo->data);
1370                 if (!is_file_exist(msgfile))
1371                         g_warning("%s: file not exist\n", msgfile);
1372                 else
1373                         compose_attach_append(compose, msgfile, msgfile,
1374                                 "message/rfc822");
1375                 g_free(msgfile);
1376         }
1377
1378         if (account->auto_sig)
1379                 compose_insert_sig(compose, FALSE);
1380
1381         compose_wrap_all(compose);
1382
1383         gtk_text_buffer_get_start_iter(textbuf, &iter);
1384         gtk_text_buffer_place_cursor(textbuf, &iter);
1385
1386         gtk_widget_grab_focus(compose->header_last->entry);
1387         
1388         compose_set_title(compose);
1389         return compose;
1390 }
1391
1392 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
1393 {
1394         GtkTextIter start = *iter;
1395         GtkTextIter end_iter;
1396         int start_pos = gtk_text_iter_get_offset(&start);
1397
1398         if (!compose->account->sig_sep)
1399                 return FALSE;
1400         
1401         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1402                 start_pos+strlen(compose->account->sig_sep));
1403
1404         /* check sig separator */
1405         if (!strcmp(gtk_text_iter_get_text(&start, &end_iter),
1406                         compose->account->sig_sep)) {
1407                 /* check end of line (\n) */
1408                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1409                         start_pos+strlen(compose->account->sig_sep));
1410                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1411                         start_pos+strlen(compose->account->sig_sep)+1);
1412
1413                 if (!strcmp(gtk_text_iter_get_text(&start, &end_iter),"\n"));
1414                         return TRUE;
1415                 
1416
1417         }
1418
1419         return FALSE;
1420 }
1421
1422 static void compose_colorize_signature(Compose *compose)
1423 {
1424         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1425         GtkTextIter iter;
1426         GtkTextIter end_iter;
1427         gtk_text_buffer_get_start_iter(buffer, &iter);
1428         while (gtk_text_iter_forward_line(&iter))
1429                 if (compose_is_sig_separator(compose, buffer, &iter)) {
1430                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
1431                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1432                 }
1433 }
1434
1435 void compose_reedit(MsgInfo *msginfo)
1436 {
1437         Compose *compose = NULL;
1438         PrefsAccount *account = NULL;
1439         GtkTextView *textview;
1440         GtkTextBuffer *textbuf;
1441         GtkTextMark *mark;
1442         GtkTextIter iter;
1443         FILE *fp;
1444         gchar buf[BUFFSIZE];
1445         gboolean use_signing = FALSE;
1446         gboolean use_encryption = FALSE;
1447         gchar *privacy_system = NULL;
1448
1449         g_return_if_fail(msginfo != NULL);
1450         g_return_if_fail(msginfo->folder != NULL);
1451
1452         if (compose_put_existing_to_front(msginfo)) 
1453                 return;
1454
1455         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1456             folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1457                 gchar queueheader_buf[BUFFSIZE];
1458                 gint id, param;
1459
1460                 /* Select Account from queue headers */
1461                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1462                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1463                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1464                         account = account_find_from_id(id);
1465                 }
1466                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1467                                              sizeof(queueheader_buf), "NAID:")) {
1468                         id = atoi(&queueheader_buf[strlen("NAID:")]);
1469                         account = account_find_from_id(id);
1470                 }
1471                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1472                                                     sizeof(queueheader_buf), "MAID:")) {
1473                         id = atoi(&queueheader_buf[strlen("MAID:")]);
1474                         account = account_find_from_id(id);
1475                 }
1476                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1477                                                                 sizeof(queueheader_buf), "S:")) {
1478                         account = account_find_from_address(queueheader_buf);
1479                 }
1480                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1481                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1482                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1483                         use_signing = param;
1484                         
1485                 }
1486                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1487                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1488                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1489                         use_encryption = param;
1490                 }
1491                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1492                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1493                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1494                 }
1495                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1496                                              sizeof(queueheader_buf), "X-Priority: ")) {
1497                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1498                         compose->priority = param;
1499                 }
1500         } else 
1501                 account = msginfo->folder->folder->account;
1502
1503         if (!account && prefs_common.reedit_account_autosel) {
1504                 gchar from[BUFFSIZE];
1505                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")){
1506                         extract_address(from);
1507                         account = account_find_from_address(from);
1508                 }
1509         }
1510         if (!account) account = cur_account;
1511         g_return_if_fail(account != NULL);
1512
1513         compose = compose_create(account, COMPOSE_REEDIT);
1514         if (privacy_system != NULL) {
1515                 compose->privacy_system = privacy_system;
1516                 compose_use_signing(compose, use_signing);
1517                 compose_use_encryption(compose, use_encryption);
1518                 compose_update_privacy_system_menu_item(compose, FALSE);
1519         } else {
1520                 activate_privacy_system(compose, account, FALSE);
1521         }
1522         compose->targetinfo = procmsg_msginfo_copy(msginfo);
1523
1524         compose_extract_original_charset(compose);
1525
1526         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1527             folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1528                 gchar queueheader_buf[BUFFSIZE];
1529
1530                 /* Set message save folder */
1531                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
1532                         gint startpos = 0;
1533
1534                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1535                         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
1536                         gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
1537                 }
1538         }
1539         
1540         if (compose_parse_header(compose, msginfo) < 0) return;
1541         compose_reedit_set_entry(compose, msginfo);
1542
1543         textview = GTK_TEXT_VIEW(compose->text);
1544         textbuf = gtk_text_view_get_buffer(textview);
1545         compose_create_tags(textview, compose);
1546
1547         mark = gtk_text_buffer_get_insert(textbuf);
1548         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1549
1550         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
1551                                         G_CALLBACK(compose_changed_cb),
1552                                         compose);
1553         
1554         if (procmime_msginfo_is_encrypted(msginfo)) {
1555                 fp = procmime_get_first_encrypted_text_content(msginfo);
1556                 if (fp) 
1557                         compose_force_encryption(compose, account, TRUE);
1558         } else
1559                 fp = procmime_get_first_text_content(msginfo);
1560         if (fp == NULL)
1561                 g_warning("Can't get text part\n");
1562
1563         if (fp != NULL) {
1564                 gboolean prev_autowrap = compose->autowrap;
1565
1566                 compose->autowrap = FALSE;
1567                 while (fgets(buf, sizeof(buf), fp) != NULL) {
1568                         strcrchomp(buf);
1569                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
1570                 }
1571                 compose_wrap_all_full(compose, FALSE);
1572                 compose->autowrap = prev_autowrap;
1573                 fclose(fp);
1574         }
1575         
1576         compose_attach_parts(compose, msginfo);
1577
1578         compose_colorize_signature(compose);
1579
1580         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
1581                                         G_CALLBACK(compose_changed_cb),
1582                                         compose);
1583
1584         gtk_widget_grab_focus(compose->text);
1585
1586         if (prefs_common.auto_exteditor)
1587                 compose_exec_ext_editor(compose);
1588         compose_set_title(compose);
1589 }
1590
1591 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo)
1592 {
1593         Compose *compose;
1594         gchar *filename;
1595         GtkItemFactory *ifactory;
1596         FolderItem *item;
1597
1598         g_return_val_if_fail(msginfo != NULL, NULL);
1599
1600         if (!account)
1601                 account = account_get_reply_account(msginfo,
1602                                         prefs_common.reply_account_autosel);
1603         g_return_val_if_fail(account != NULL, NULL);
1604
1605         compose = compose_create(account, COMPOSE_REDIRECT);
1606         ifactory = gtk_item_factory_from_widget(compose->menubar);
1607         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
1608         compose->replyinfo = NULL;
1609         compose->fwdinfo = NULL;
1610
1611         compose_show_first_last_header(compose, TRUE);
1612
1613         gtk_widget_grab_focus(compose->header_last->entry);
1614
1615         filename = procmsg_get_message_file_path(msginfo);
1616         if (filename == NULL)
1617                 return NULL;
1618
1619         compose->redirect_filename = filename;
1620         
1621         /* Set save folder */
1622         item = msginfo->folder;
1623         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1624                 gchar *folderidentifier;
1625
1626                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1627                 folderidentifier = folder_item_get_identifier(item);
1628                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1629                 g_free(folderidentifier);
1630         }
1631
1632         compose_attach_parts(compose, msginfo);
1633
1634         if (msginfo->subject)
1635                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1636                                    msginfo->subject);
1637         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
1638
1639         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL);
1640         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
1641
1642         compose_colorize_signature(compose);
1643
1644         ifactory = gtk_item_factory_from_widget(compose->popupmenu);
1645         menu_set_sensitive(ifactory, "/Add...", FALSE);
1646         menu_set_sensitive(ifactory, "/Remove", FALSE);
1647         menu_set_sensitive(ifactory, "/Properties...", FALSE);
1648
1649         ifactory = gtk_item_factory_from_widget(compose->menubar);
1650         menu_set_sensitive(ifactory, "/Message/Save", FALSE);
1651         menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
1652         menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
1653         menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
1654         menu_set_sensitive(ifactory, "/Edit", FALSE);
1655         menu_set_sensitive(ifactory, "/Options", FALSE);
1656         menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
1657         menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
1658         
1659         gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
1660         gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
1661         gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
1662         gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
1663         gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
1664         gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
1665         gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
1666
1667         compose_set_title(compose);
1668         return compose;
1669 }
1670
1671 GList *compose_get_compose_list(void)
1672 {
1673         return compose_list;
1674 }
1675
1676 void compose_entry_append(Compose *compose, const gchar *address,
1677                           ComposeEntryType type)
1678 {
1679         gchar *header;
1680         gchar *cur, *begin;
1681         gboolean in_quote = FALSE;
1682         if (!address || *address == '\0') return;
1683
1684         switch (type) {
1685         case COMPOSE_CC:
1686                 header = N_("Cc:");
1687                 break;
1688         case COMPOSE_BCC:
1689                 header = N_("Bcc:");
1690                 break;
1691         case COMPOSE_REPLYTO:
1692                 header = N_("Reply-To:");
1693                 break;
1694         case COMPOSE_NEWSGROUPS:
1695                 header = N_("Newsgroups:");
1696                 break;
1697         case COMPOSE_FOLLOWUPTO:
1698                 header = N_( "Followup-To:");
1699                 break;
1700         case COMPOSE_TO:
1701         default:
1702                 header = N_("To:");
1703                 break;
1704         }
1705         header = prefs_common.trans_hdr ? gettext(header) : header;
1706         
1707         cur = begin = (gchar *)address;
1708         
1709         /* we separate the line by commas, but not if we're inside a quoted
1710          * string */
1711         while (*cur != '\0') {
1712                 if (*cur == '"') 
1713                         in_quote = !in_quote;
1714                 if (*cur == ',' && !in_quote) {
1715                         gchar *tmp = g_strdup(begin);
1716                         gchar *o_tmp = tmp;
1717                         tmp[cur-begin]='\0';
1718                         cur++;
1719                         begin = cur;
1720                         while (*tmp == ' ')
1721                                 tmp++;
1722                         compose_add_header_entry(compose, header, tmp);
1723                         g_free(o_tmp);
1724                         continue;
1725                 }
1726                 cur++;
1727         }
1728         if (begin < cur) {
1729                 gchar *tmp = g_strdup(begin);
1730                 gchar *o_tmp = tmp;
1731                 tmp[cur-begin]='\0';
1732                 cur++;
1733                 begin = cur;
1734                 while (*tmp == ' ')
1735                         tmp++;
1736                 compose_add_header_entry(compose, header, tmp);
1737                 g_free(o_tmp);          
1738         }
1739 }
1740
1741 void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
1742 {
1743         static GtkStyle *bold_style = NULL;
1744         static GdkColor bold_color;
1745         GSList *h_list;
1746         GtkEntry *entry;
1747                 
1748         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
1749                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
1750                 if (gtk_entry_get_text(entry) && 
1751                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
1752                         gtk_widget_ensure_style(GTK_WIDGET(entry));
1753                         if (!bold_style) {
1754                                 gtkut_convert_int_to_gdk_color
1755                                         (prefs_common.color_new, &bold_color);
1756                                 bold_style = gtk_style_copy(gtk_widget_get_style
1757                                         (GTK_WIDGET(entry)));
1758                                 pango_font_description_set_weight
1759                                         (bold_style->font_desc, PANGO_WEIGHT_BOLD);
1760                                 bold_style->fg[GTK_STATE_NORMAL] = bold_color;
1761                         }
1762                         gtk_widget_set_style(GTK_WIDGET(entry), bold_style);
1763                 }
1764         }
1765 }
1766
1767 void compose_toolbar_cb(gint action, gpointer data)
1768 {
1769         ToolbarItem *toolbar_item = (ToolbarItem*)data;
1770         Compose *compose = (Compose*)toolbar_item->parent;
1771         
1772         g_return_if_fail(compose != NULL);
1773
1774         switch(action) {
1775         case A_SEND:
1776                 compose_send_cb(compose, 0, NULL);
1777                 break;
1778         case A_SENDL:
1779                 compose_send_later_cb(compose, 0, NULL);
1780                 break;
1781         case A_DRAFT:
1782                 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
1783                 break;
1784         case A_INSERT:
1785                 compose_insert_file_cb(compose, 0, NULL);
1786                 break;
1787         case A_ATTACH:
1788                 compose_attach_cb(compose, 0, NULL);
1789                 break;
1790         case A_SIG:
1791                 compose_insert_sig(compose, FALSE);
1792                 break;
1793         case A_EXTEDITOR:
1794                 compose_ext_editor_cb(compose, 0, NULL);
1795                 break;
1796         case A_LINEWRAP_CURRENT:
1797                 compose_beautify_paragraph(compose, NULL, TRUE);
1798                 break;
1799         case A_LINEWRAP_ALL:
1800                 compose_wrap_all_full(compose, TRUE);
1801                 break;
1802         case A_ADDRBOOK:
1803                 compose_address_cb(compose, 0, NULL);
1804                 break;
1805 #ifdef USE_ASPELL
1806         case A_CHECK_SPELLING:
1807                 compose_check_all(compose);
1808                 break;
1809 #endif
1810         default:
1811                 break;
1812         }
1813 }
1814
1815 static void compose_entries_set(Compose *compose, const gchar *mailto)
1816 {
1817         gchar *to = NULL;
1818         gchar *cc = NULL;
1819         gchar *subject = NULL;
1820         gchar *body = NULL;
1821         gchar *temp = NULL;
1822         gint  len = 0;
1823
1824         scan_mailto_url(mailto, &to, &cc, NULL, &subject, &body);
1825
1826         if (to)
1827                 compose_entry_append(compose, to, COMPOSE_TO);
1828         if (cc)
1829                 compose_entry_append(compose, cc, COMPOSE_CC);
1830         if (subject) {
1831                 if (!g_utf8_validate (subject, -1, NULL)) {
1832                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
1833                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
1834                         g_free(temp);
1835                 } else {
1836                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
1837                 }
1838         }
1839         if (body) {
1840                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1841                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
1842                 GtkTextMark *mark;
1843                 GtkTextIter iter;
1844                 gboolean prev_autowrap = compose->autowrap;
1845
1846                 compose->autowrap = FALSE;
1847
1848                 mark = gtk_text_buffer_get_insert(buffer);
1849                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1850
1851                 if (!g_utf8_validate (body, -1, NULL)) {
1852                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
1853                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
1854                         g_free(temp);
1855                 } else {
1856                         gtk_text_buffer_insert(buffer, &iter, body, -1);
1857                 }
1858                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1859
1860                 compose->autowrap = prev_autowrap;
1861                 if (compose->autowrap)
1862                         compose_wrap_all(compose);
1863         }
1864
1865         g_free(to);
1866         g_free(cc);
1867         g_free(subject);
1868         g_free(body);
1869 }
1870
1871 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1872 {
1873         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
1874                                        {"Cc:",          NULL, TRUE},
1875                                        {"References:",  NULL, FALSE},
1876                                        {"Bcc:",         NULL, TRUE},
1877                                        {"Newsgroups:",  NULL, TRUE},
1878                                        {"Followup-To:", NULL, TRUE},
1879                                        {"List-Post:",   NULL, FALSE},
1880                                        {"X-Priority:",  NULL, FALSE},
1881                                        {NULL,           NULL, FALSE}};
1882
1883         enum
1884         {
1885                 H_REPLY_TO      = 0,
1886                 H_CC            = 1,
1887                 H_REFERENCES    = 2,
1888                 H_BCC           = 3,
1889                 H_NEWSGROUPS    = 4,
1890                 H_FOLLOWUP_TO   = 5,
1891                 H_LIST_POST     = 6,
1892                 H_X_PRIORITY    = 7
1893         };
1894
1895         FILE *fp;
1896
1897         g_return_val_if_fail(msginfo != NULL, -1);
1898
1899         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1900         procheader_get_header_fields(fp, hentry);
1901         fclose(fp);
1902
1903         if (hentry[H_REPLY_TO].body != NULL) {
1904                 if (hentry[H_REPLY_TO].body[0] != '\0') {
1905                         compose->replyto =
1906                                 conv_unmime_header(hentry[H_REPLY_TO].body,
1907                                                    NULL);
1908                 }
1909                 g_free(hentry[H_REPLY_TO].body);
1910                 hentry[H_REPLY_TO].body = NULL;
1911         }
1912         if (hentry[H_CC].body != NULL) {
1913                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
1914                 g_free(hentry[H_CC].body);
1915                 hentry[H_CC].body = NULL;
1916         }
1917         if (hentry[H_REFERENCES].body != NULL) {
1918                 if (compose->mode == COMPOSE_REEDIT)
1919                         compose->references = hentry[H_REFERENCES].body;
1920                 else {
1921                         compose->references = compose_parse_references
1922                                 (hentry[H_REFERENCES].body, msginfo->msgid);
1923                         g_free(hentry[H_REFERENCES].body);
1924                 }
1925                 hentry[H_REFERENCES].body = NULL;
1926         }
1927         if (hentry[H_BCC].body != NULL) {
1928                 if (compose->mode == COMPOSE_REEDIT)
1929                         compose->bcc =
1930                                 conv_unmime_header(hentry[H_BCC].body, NULL);
1931                 g_free(hentry[H_BCC].body);
1932                 hentry[H_BCC].body = NULL;
1933         }
1934         if (hentry[H_NEWSGROUPS].body != NULL) {
1935                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
1936                 hentry[H_NEWSGROUPS].body = NULL;
1937         }
1938         if (hentry[H_FOLLOWUP_TO].body != NULL) {
1939                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
1940                         compose->followup_to =
1941                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
1942                                                    NULL);
1943                 }
1944                 g_free(hentry[H_FOLLOWUP_TO].body);
1945                 hentry[H_FOLLOWUP_TO].body = NULL;
1946         }
1947         if (hentry[H_LIST_POST].body != NULL) {
1948                 gchar *to = NULL;
1949
1950                 extract_address(hentry[H_LIST_POST].body);
1951                 if (hentry[H_LIST_POST].body[0] != '\0') {
1952                         scan_mailto_url(hentry[H_LIST_POST].body,
1953                                         &to, NULL, NULL, NULL, NULL);
1954                         if (to) {
1955                                 g_free(compose->ml_post);
1956                                 compose->ml_post = to;
1957                         }
1958                 }
1959                 g_free(hentry[H_LIST_POST].body);
1960                 hentry[H_LIST_POST].body = NULL;
1961         }
1962
1963         /* CLAWS - X-Priority */
1964         if (compose->mode == COMPOSE_REEDIT)
1965                 if (hentry[H_X_PRIORITY].body != NULL) {
1966                         gint priority;
1967                         
1968                         priority = atoi(hentry[H_X_PRIORITY].body);
1969                         g_free(hentry[H_X_PRIORITY].body);
1970                         
1971                         hentry[H_X_PRIORITY].body = NULL;
1972                         
1973                         if (priority < PRIORITY_HIGHEST || 
1974                             priority > PRIORITY_LOWEST)
1975                                 priority = PRIORITY_NORMAL;
1976                         
1977                         compose->priority =  priority;
1978                 }
1979  
1980         if (compose->mode == COMPOSE_REEDIT) {
1981                 if (msginfo->inreplyto && *msginfo->inreplyto)
1982                         compose->inreplyto = g_strdup(msginfo->inreplyto);
1983                 return 0;
1984         }
1985
1986         if (msginfo->msgid && *msginfo->msgid)
1987                 compose->inreplyto = g_strdup(msginfo->msgid);
1988
1989         if (!compose->references) {
1990                 if (msginfo->msgid && *msginfo->msgid) {
1991                         if (msginfo->inreplyto && *msginfo->inreplyto)
1992                                 compose->references =
1993                                         g_strdup_printf("<%s>\n\t<%s>",
1994                                                         msginfo->inreplyto,
1995                                                         msginfo->msgid);
1996                         else
1997                                 compose->references =
1998                                         g_strconcat("<", msginfo->msgid, ">",
1999                                                     NULL);
2000                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2001                         compose->references =
2002                                 g_strconcat("<", msginfo->inreplyto, ">",
2003                                             NULL);
2004                 }
2005         }
2006
2007         return 0;
2008 }
2009
2010 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2011 {
2012         GSList *ref_id_list, *cur;
2013         GString *new_ref;
2014         gchar *new_ref_str;
2015
2016         ref_id_list = references_list_append(NULL, ref);
2017         if (!ref_id_list) return NULL;
2018         if (msgid && *msgid)
2019                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2020
2021         for (;;) {
2022                 gint len = 0;
2023
2024                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2025                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2026                         len += strlen((gchar *)cur->data) + 5;
2027
2028                 if (len > MAX_REFERENCES_LEN) {
2029                         /* remove second message-ID */
2030                         if (ref_id_list && ref_id_list->next &&
2031                             ref_id_list->next->next) {
2032                                 g_free(ref_id_list->next->data);
2033                                 ref_id_list = g_slist_remove
2034                                         (ref_id_list, ref_id_list->next->data);
2035                         } else {
2036                                 slist_free_strings(ref_id_list);
2037                                 g_slist_free(ref_id_list);
2038                                 return NULL;
2039                         }
2040                 } else
2041                         break;
2042         }
2043
2044         new_ref = g_string_new("");
2045         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2046                 if (new_ref->len > 0)
2047                         g_string_append(new_ref, "\n\t");
2048                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2049         }
2050
2051         slist_free_strings(ref_id_list);
2052         g_slist_free(ref_id_list);
2053
2054         new_ref_str = new_ref->str;
2055         g_string_free(new_ref, FALSE);
2056
2057         return new_ref_str;
2058 }
2059
2060 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2061                                 const gchar *fmt, const gchar *qmark,
2062                                 const gchar *body)
2063 {
2064         static MsgInfo dummyinfo;
2065         gchar *quote_str = NULL;
2066         gchar *buf;
2067         gchar *p, *lastp;
2068         gint len;
2069         gboolean prev_autowrap;
2070         const gchar *trimmed_body = body;
2071         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2072         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2073         
2074         if (!msginfo)
2075                 msginfo = &dummyinfo;
2076
2077         if (qmark != NULL) {
2078                 quote_fmt_init(msginfo, NULL, NULL);
2079                 quote_fmt_scan_string(qmark);
2080                 quote_fmt_parse();
2081
2082                 buf = quote_fmt_get_buffer();
2083                 if (buf == NULL)
2084                         alertpanel_error(_("Quote mark format error."));
2085                 else
2086                         Xstrdup_a(quote_str, buf, return NULL)
2087         }
2088
2089         if (fmt && *fmt != '\0') {
2090                 while (trimmed_body && strlen(trimmed_body) > 1
2091                         && trimmed_body[0]=='\n')
2092                         *trimmed_body++;
2093
2094                 quote_fmt_init(msginfo, quote_str, trimmed_body);
2095                 quote_fmt_scan_string(fmt);
2096                 quote_fmt_parse();
2097
2098                 buf = quote_fmt_get_buffer();
2099                 if (buf == NULL) {
2100                         alertpanel_error(_("Message reply/forward format error."));
2101                         return NULL;
2102                 }
2103         } else
2104                 buf = "";
2105
2106         prev_autowrap = compose->autowrap;
2107         compose->autowrap = FALSE;
2108
2109         g_signal_handlers_block_by_func(G_OBJECT(buffer),
2110                                 G_CALLBACK(compose_changed_cb),
2111                                 compose);
2112         g_signal_handlers_block_by_func(G_OBJECT(buffer),
2113                                 G_CALLBACK(text_inserted),
2114                                 compose);
2115         for (p = buf; *p != '\0'; ) {
2116                 GtkTextMark *mark;
2117                 GtkTextIter iter;
2118
2119                 
2120                 mark = gtk_text_buffer_get_insert(buffer);
2121                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2122
2123                 lastp = strchr(p, '\n');
2124                 len = lastp ? lastp - p + 1 : -1;
2125
2126                 if (g_utf8_validate(p, -1, NULL)) { 
2127                         gtk_text_buffer_insert(buffer, &iter, p, len);
2128                 } else {
2129                         gchar *tmpin = g_strdup(p);
2130                         gchar *tmpout = NULL;
2131                         tmpin[len] = '\0';
2132                         tmpout = conv_codeset_strdup
2133                                 (tmpin, conv_get_locale_charset_str(),
2134                                  CS_INTERNAL);
2135                         gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2136                         g_free(tmpin);
2137                         g_free(tmpout);
2138                 }
2139
2140                 if (lastp)
2141                         p = lastp + 1;
2142                 else
2143                         break;
2144         }
2145
2146         compose->autowrap = prev_autowrap;
2147         if (compose->autowrap)
2148                 compose_wrap_all(compose);
2149
2150         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
2151                                 G_CALLBACK(compose_changed_cb),
2152                                 compose);
2153         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
2154                                 G_CALLBACK(text_inserted),
2155                                 compose);
2156
2157
2158         return buf;
2159 }
2160
2161 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2162                                     gboolean to_all, gboolean to_ml,
2163                                     gboolean to_sender,
2164                                     gboolean followup_and_reply_to)
2165 {
2166         GSList *cc_list = NULL;
2167         GSList *cur;
2168         gchar *from = NULL;
2169         gchar *replyto = NULL;
2170         GHashTable *to_table;
2171
2172         g_return_if_fail(compose->account != NULL);
2173         g_return_if_fail(msginfo != NULL);
2174
2175         if (compose->account->protocol != A_NNTP) {
2176                 if (to_ml && compose->ml_post
2177                     && !(msginfo->folder && 
2178                          msginfo->folder->prefs->enable_default_reply_to)) {
2179                         compose_entry_append(compose,
2180                                            compose->ml_post,
2181                                            COMPOSE_TO);
2182                         if (compose->replyto) {
2183                                 gchar *tmp1 = NULL, *tmp2 = NULL;
2184                                 Xstrdup_a(tmp1, compose->replyto, return);
2185                                 if (compose->ml_post)
2186                                         Xstrdup_a(tmp2, compose->ml_post, return);
2187                                 extract_address(tmp1);
2188                                 extract_address(tmp2);
2189                                 if (tmp1 && tmp2 && strcmp(tmp1, tmp2))
2190                                         compose_entry_append(compose,
2191                                                         compose->replyto,
2192                                                         COMPOSE_CC);
2193                         }
2194                 }
2195                 else if (!(to_all || to_sender)
2196                          && msginfo->folder
2197                          && msginfo->folder->prefs->enable_default_reply_to) {
2198                         compose_entry_append(compose,
2199                             msginfo->folder->prefs->default_reply_to,
2200                             COMPOSE_TO);
2201                 } else
2202                         compose_entry_append(compose,
2203                                  (compose->replyto && !to_sender)
2204                                   ? compose->replyto :
2205                                   msginfo->from ? msginfo->from : "",
2206                                   COMPOSE_TO);
2207         } else {
2208                 if (to_sender || (compose->followup_to && 
2209                         !strncmp(compose->followup_to, "poster", 6)))
2210                         compose_entry_append
2211                                 (compose, 
2212                                  (compose->replyto ? compose->replyto :
2213                                         msginfo->from ? msginfo->from : ""),
2214                                  COMPOSE_TO);
2215                                  
2216                 else if (followup_and_reply_to || to_all) {
2217                         compose_entry_append
2218                                 (compose,
2219                                  (compose->replyto ? compose->replyto :
2220                                  msginfo->from ? msginfo->from : ""),
2221                                  COMPOSE_TO);                           
2222                 
2223                         compose_entry_append
2224                                 (compose,
2225                                  compose->followup_to ? compose->followup_to :
2226                                  compose->newsgroups ? compose->newsgroups : "",
2227                                  COMPOSE_NEWSGROUPS);
2228                 } 
2229                 else 
2230                         compose_entry_append
2231                                 (compose,
2232                                  compose->followup_to ? compose->followup_to :
2233                                  compose->newsgroups ? compose->newsgroups : "",
2234                                  COMPOSE_NEWSGROUPS);
2235         }
2236
2237         if (msginfo->subject && *msginfo->subject) {
2238                 gchar *buf, *buf2;
2239                 guchar *p;
2240
2241                 buf = p = g_strdup(msginfo->subject);
2242                 p += subject_get_prefix_length(p);
2243                 memmove(buf, p, strlen(p) + 1);
2244
2245                 buf2 = g_strdup_printf("Re: %s", buf);
2246                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2247
2248                 g_free(buf2);
2249                 g_free(buf);
2250         } else
2251                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2252
2253         if (to_ml && compose->ml_post) return;
2254         if (!to_all || compose->account->protocol == A_NNTP) return;
2255
2256         if (compose->replyto) {
2257                 Xstrdup_a(replyto, compose->replyto, return);
2258                 extract_address(replyto);
2259         }
2260         if (msginfo->from) {
2261                 Xstrdup_a(from, msginfo->from, return);
2262                 extract_address(from);
2263         }
2264
2265         if (replyto && from)
2266                 cc_list = address_list_append_with_comments(cc_list, from);
2267         if (to_all && msginfo->folder && 
2268             msginfo->folder->prefs->enable_default_reply_to)
2269                 cc_list = address_list_append_with_comments(cc_list,
2270                                 msginfo->folder->prefs->default_reply_to);
2271         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2272         cc_list = address_list_append_with_comments(cc_list, compose->cc);
2273
2274         to_table = g_hash_table_new(g_str_hash, g_str_equal);
2275         if (replyto)
2276                 g_hash_table_insert(to_table, g_strdup(replyto), GINT_TO_POINTER(1));
2277         if (compose->account)
2278                 g_hash_table_insert(to_table, g_strdup(compose->account->address),
2279                                     GINT_TO_POINTER(1));
2280
2281         /* remove address on To: and that of current account */
2282         for (cur = cc_list; cur != NULL; ) {
2283                 GSList *next = cur->next;
2284                 gchar *addr;
2285
2286                 addr = g_strdup(cur->data);
2287                 extract_address(addr);
2288
2289                 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2290                         cc_list = g_slist_remove(cc_list, cur->data);
2291                 else
2292                         g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2293
2294                 cur = next;
2295         }
2296         hash_free_strings(to_table);
2297         g_hash_table_destroy(to_table);
2298
2299         if (cc_list) {
2300                 for (cur = cc_list; cur != NULL; cur = cur->next)
2301                         compose_entry_append(compose, (gchar *)cur->data,
2302                                              COMPOSE_CC);
2303                 slist_free_strings(cc_list);
2304                 g_slist_free(cc_list);
2305         }
2306
2307 }
2308
2309 #define SET_ENTRY(entry, str) \
2310 { \
2311         if (str && *str) \
2312                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
2313 }
2314
2315 #define SET_ADDRESS(type, str) \
2316 { \
2317         if (str && *str) \
2318                 compose_entry_append(compose, str, type); \
2319 }
2320
2321 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
2322 {
2323         g_return_if_fail(msginfo != NULL);
2324
2325         SET_ENTRY(subject_entry, msginfo->subject);
2326         SET_ADDRESS(COMPOSE_TO, msginfo->to);
2327         SET_ADDRESS(COMPOSE_CC, compose->cc);
2328         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
2329         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
2330         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
2331         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
2332
2333         compose_update_priority_menu_item(compose);
2334         compose_update_privacy_system_menu_item(compose, FALSE);
2335         compose_show_first_last_header(compose, TRUE);
2336 }
2337
2338 #undef SET_ENTRY
2339 #undef SET_ADDRESS
2340
2341 static void compose_insert_sig(Compose *compose, gboolean replace)
2342 {
2343         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2344         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2345         GtkTextMark *mark;
2346         GtkTextIter iter, iter_end;
2347         gint cur_pos;
2348         gboolean prev_autowrap;
2349
2350         
2351         g_return_if_fail(compose->account != NULL);
2352
2353         prev_autowrap = compose->autowrap;
2354         compose->autowrap = FALSE;
2355
2356         g_signal_handlers_block_by_func(G_OBJECT(buffer),
2357                                         G_CALLBACK(compose_changed_cb),
2358                                         compose);
2359         
2360         mark = gtk_text_buffer_get_insert(buffer);
2361         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2362         cur_pos = gtk_text_iter_get_offset (&iter);
2363
2364         gtk_text_buffer_get_end_iter(buffer, &iter);
2365
2366         if (replace && compose->sig_str) {
2367                 gboolean found;
2368                 GtkTextIter first_iter, start_iter, end_iter;
2369
2370                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
2371
2372                 if (compose->sig_str[0] == '\0')
2373                         found = FALSE;
2374                 else
2375                         found = gtk_text_iter_forward_search(&first_iter,
2376                                                              compose->sig_str,
2377                                                              GTK_TEXT_SEARCH_TEXT_ONLY,
2378                                                              &start_iter, &end_iter,
2379                                                              NULL);
2380
2381                 if (found) {
2382                         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
2383                         iter = start_iter;
2384                 }
2385         }
2386
2387         g_free(compose->sig_str);
2388         compose->sig_str = compose_get_signature_str(compose);
2389         if (!compose->sig_str || (replace && !compose->account->auto_sig))
2390                 compose->sig_str = g_strdup("");
2391
2392         cur_pos = gtk_text_iter_get_offset(&iter);
2393         gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
2394         /* skip \n\n */
2395         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
2396         gtk_text_iter_forward_char(&iter);
2397         gtk_text_iter_forward_char(&iter);
2398         gtk_text_buffer_get_end_iter(buffer, &iter_end);
2399         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
2400
2401         if (cur_pos > gtk_text_buffer_get_char_count (buffer))
2402                 cur_pos = gtk_text_buffer_get_char_count (buffer);
2403
2404         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
2405         gtk_text_buffer_place_cursor(buffer, &iter);
2406
2407         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
2408                                         G_CALLBACK(compose_changed_cb),
2409                                         compose);
2410                 
2411         compose->autowrap = prev_autowrap;
2412         if (compose->autowrap)
2413                 compose_wrap_all(compose);
2414 }
2415
2416 static gchar *compose_get_signature_str(Compose *compose)
2417 {
2418         gchar *sig_body = NULL;
2419         gchar *sig_str = NULL;
2420         gchar *utf8_sig_str = NULL;
2421
2422         g_return_val_if_fail(compose->account != NULL, NULL);
2423
2424         if (!compose->account->sig_path)
2425                 return NULL;
2426
2427         if (compose->account->sig_type == SIG_FILE) {
2428                 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
2429                         g_warning("can't open signature file: %s\n",
2430                                   compose->account->sig_path);
2431                         return NULL;
2432                 }
2433         }
2434
2435         if (compose->account->sig_type == SIG_COMMAND)
2436                 sig_body = get_command_output(compose->account->sig_path);
2437         else {
2438                 gchar *tmp;
2439
2440                 tmp = file_read_to_str(compose->account->sig_path);
2441                 if (!tmp)
2442                         return NULL;
2443                 sig_body = normalize_newlines(tmp);
2444                 g_free(tmp);
2445         }
2446
2447         if (compose->account->sig_sep) {
2448                 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
2449                                       NULL);
2450                 g_free(sig_body);
2451         } else
2452                 sig_str = g_strconcat("\n\n", sig_body, NULL);
2453
2454         if (sig_str) {
2455                 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
2456                         utf8_sig_str = sig_str;
2457                 else {
2458                         utf8_sig_str = conv_codeset_strdup
2459                                 (sig_str, conv_get_locale_charset_str(),
2460                                  CS_INTERNAL);
2461                         g_free(sig_str);
2462                 }
2463         }
2464
2465         return utf8_sig_str;
2466 }
2467
2468 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
2469 {
2470         GtkTextView *text;
2471         GtkTextBuffer *buffer;
2472         GtkTextMark *mark;
2473         GtkTextIter iter;
2474         const gchar *cur_encoding;
2475         gchar buf[BUFFSIZE];
2476         gint len;
2477         FILE *fp;
2478         gboolean prev_autowrap;
2479         gboolean badtxt = FALSE;
2480
2481         g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
2482
2483         if ((fp = g_fopen(file, "rb")) == NULL) {
2484                 FILE_OP_ERROR(file, "fopen");
2485                 return COMPOSE_INSERT_READ_ERROR;
2486         }
2487
2488         prev_autowrap = compose->autowrap;
2489         compose->autowrap = FALSE;
2490
2491         text = GTK_TEXT_VIEW(compose->text);
2492         buffer = gtk_text_view_get_buffer(text);
2493         mark = gtk_text_buffer_get_insert(buffer);
2494         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2495
2496         g_signal_handlers_block_by_func(G_OBJECT(buffer),
2497                                         G_CALLBACK(text_inserted),
2498                                         compose);
2499
2500         cur_encoding = conv_get_locale_charset_str();
2501
2502         while (fgets(buf, sizeof(buf), fp) != NULL) {
2503                 gchar *str;
2504
2505                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
2506                         str = g_strdup(buf);
2507                 else
2508                         str = conv_codeset_strdup
2509                                 (buf, cur_encoding, CS_INTERNAL);
2510                 if (!str) continue;
2511
2512                 /* strip <CR> if DOS/Windows file,
2513                    replace <CR> with <LF> if Macintosh file. */
2514                 strcrchomp(str);
2515                 len = strlen(str);
2516                 if (len > 0 && str[len - 1] != '\n') {
2517                         while (--len >= 0)
2518                                 if (str[len] == '\r') str[len] = '\n';
2519                 }
2520
2521                 gtk_text_buffer_insert(buffer, &iter, str, -1);
2522                 g_free(str);
2523         }
2524
2525         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
2526                                           G_CALLBACK(text_inserted),
2527                                           compose);
2528         compose->autowrap = prev_autowrap;
2529         if (compose->autowrap)
2530                 compose_wrap_all(compose);
2531
2532         fclose(fp);
2533
2534         if (badtxt)
2535                 return COMPOSE_INSERT_INVALID_CHARACTER;
2536         else 
2537                 return COMPOSE_INSERT_SUCCESS;
2538 }
2539
2540 static void compose_attach_append(Compose *compose, const gchar *file,
2541                                   const gchar *filename,
2542                                   const gchar *content_type)
2543 {
2544         AttachInfo *ainfo;
2545         GtkTreeIter iter;
2546         FILE *fp;
2547         off_t size;
2548         GAuto *auto_ainfo;
2549         gchar *size_text;
2550         GtkListStore *store;
2551         gchar *name;
2552
2553         if (!is_file_exist(file)) {
2554                 g_warning("File %s doesn't exist\n", filename);
2555                 return;
2556         }
2557         if ((size = get_file_size(file)) < 0) {
2558                 g_warning("Can't get file size of %s\n", filename);
2559                 return;
2560         }
2561         if (size == 0) {
2562                 alertpanel_notice(_("File %s is empty."), filename);
2563                 return;
2564         }
2565         if ((fp = g_fopen(file, "rb")) == NULL) {
2566                 alertpanel_error(_("Can't read %s."), filename);
2567                 return;
2568         }
2569         fclose(fp);
2570
2571         ainfo = g_new0(AttachInfo, 1);
2572         auto_ainfo = g_auto_pointer_new_with_free
2573                         (ainfo, (GFreeFunc) compose_attach_info_free); 
2574         ainfo->file = g_strdup(file);
2575
2576         if (content_type) {
2577                 ainfo->content_type = g_strdup(content_type);
2578                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
2579                         MsgInfo *msginfo;
2580                         MsgFlags flags = {0, 0};
2581
2582                         if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
2583                                 ainfo->encoding = ENC_7BIT;
2584                         else
2585                                 ainfo->encoding = ENC_8BIT;
2586
2587                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2588                         if (msginfo && msginfo->subject)
2589                                 name = g_strdup(msginfo->subject);
2590                         else
2591                                 name = g_path_get_basename(filename ? filename : file);
2592
2593                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
2594
2595                         procmsg_msginfo_free(msginfo);
2596                 } else {
2597                         if (!g_ascii_strncasecmp(content_type, "text", 4))
2598                                 ainfo->encoding = procmime_get_encoding_for_text_file(file);
2599                         else
2600                                 ainfo->encoding = ENC_BASE64;
2601                         name = g_path_get_basename(filename ? filename : file);
2602                         ainfo->name = g_strdup(name);
2603                 }
2604                 g_free(name);
2605         } else {
2606                 ainfo->content_type = procmime_get_mime_type(file);
2607                 if (!ainfo->content_type) {
2608                         ainfo->content_type =
2609                                 g_strdup("application/octet-stream");
2610                         ainfo->encoding = ENC_BASE64;
2611                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
2612                         ainfo->encoding =
2613                                 procmime_get_encoding_for_text_file(file);
2614                 else
2615                         ainfo->encoding = ENC_BASE64;
2616                 name = g_path_get_basename(filename ? filename : file);
2617                 ainfo->name = g_strdup(name);   
2618                 g_free(name);
2619         }
2620
2621         if (!strcmp(ainfo->content_type, "unknown")) {
2622                 g_free(ainfo->content_type);
2623                 ainfo->content_type = g_strdup("application/octet-stream");
2624         }
2625
2626         ainfo->size = size;
2627         size_text = to_human_readable(size);
2628
2629         store = GTK_LIST_STORE(gtk_tree_view_get_model
2630                         (GTK_TREE_VIEW(compose->attach_clist)));
2631                 
2632         gtk_list_store_append(store, &iter);
2633         gtk_list_store_set(store, &iter, 
2634                            COL_MIMETYPE, ainfo->content_type,
2635                            COL_SIZE, size_text,
2636                            COL_NAME, ainfo->name,
2637                            COL_DATA, ainfo,
2638                            COL_AUTODATA, auto_ainfo,
2639                            -1);
2640         
2641         g_auto_pointer_free(auto_ainfo);
2642 }
2643
2644 static void compose_use_signing(Compose *compose, gboolean use_signing)
2645 {
2646         GtkItemFactory *ifactory;
2647         GtkWidget *menuitem = NULL;
2648
2649         compose->use_signing = use_signing;
2650         ifactory = gtk_item_factory_from_widget(compose->menubar);
2651         menuitem = gtk_item_factory_get_item
2652                 (ifactory, "/Options/Sign");
2653         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
2654                                        use_signing);
2655 }
2656
2657 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
2658 {
2659         GtkItemFactory *ifactory;
2660         GtkWidget *menuitem = NULL;
2661
2662         compose->use_encryption = use_encryption;
2663         ifactory = gtk_item_factory_from_widget(compose->menubar);
2664         menuitem = gtk_item_factory_get_item
2665                 (ifactory, "/Options/Encrypt");
2666
2667         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
2668                                        use_encryption);
2669 }
2670
2671 #define NEXT_PART_NOT_CHILD(info)  \
2672 {  \
2673         node = info->node;  \
2674         while (node->children)  \
2675                 node = g_node_last_child(node);  \
2676         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
2677 }
2678
2679 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
2680 {
2681         MimeInfo *mimeinfo;
2682         MimeInfo *child;
2683         MimeInfo *firsttext = NULL;
2684         MimeInfo *encrypted = NULL;
2685         GNode    *node;
2686         gchar *outfile;
2687         const gchar *partname = NULL;
2688
2689         mimeinfo = procmime_scan_message(msginfo);
2690         if (!mimeinfo) return;
2691
2692         if (mimeinfo->node->children == NULL) {
2693                 procmime_mimeinfo_free_all(mimeinfo);
2694                 return;
2695         }
2696
2697         /* find first content part */
2698         child = (MimeInfo *) mimeinfo->node->children->data;
2699         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
2700                 child = (MimeInfo *)child->node->children->data;
2701
2702         if (child->type == MIMETYPE_TEXT) {
2703                 firsttext = child;
2704                 debug_print("First text part found\n");
2705         } else if (compose->mode == COMPOSE_REEDIT &&
2706                  child->type == MIMETYPE_APPLICATION &&
2707                  !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
2708                 encrypted = (MimeInfo *)child->node->parent->data;
2709         }
2710      
2711         child = (MimeInfo *) mimeinfo->node->children->data;
2712         while (child != NULL) {
2713                 if (child == encrypted) {
2714                         /* skip this part of tree */
2715                         NEXT_PART_NOT_CHILD(child);
2716                         continue;
2717                 }
2718
2719                 if (child->type == MIMETYPE_MULTIPART) {
2720                         /* get the actual content */
2721                         child = procmime_mimeinfo_next(child);
2722                         continue;
2723                 }
2724                     
2725                 if (child == firsttext) {
2726                         child = procmime_mimeinfo_next(child);
2727                         continue;
2728                 }
2729
2730                 outfile = procmime_get_tmp_file_name(child);
2731                 if (procmime_get_part(outfile, child) < 0)
2732                         g_warning("Can't get the part of multipart message.");
2733                 else {
2734                         gchar *content_type;
2735
2736                         content_type = procmime_get_content_type_str(child->type, child->subtype);
2737
2738                         /* if we meet a pgp signature, we don't attach it, but
2739                          * we force signing. */
2740                         if (strcmp(content_type, "application/pgp-signature")) {
2741                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
2742                                 if (partname == NULL)
2743                                         partname = procmime_mimeinfo_get_parameter(child, "name");
2744                                 if (partname == NULL)
2745                                         partname = "";
2746                                 compose_attach_append(compose, outfile, 
2747                                                       partname, content_type);
2748                         } else {
2749                                 compose_force_signing(compose, compose->account);
2750                         }
2751                         g_free(content_type);
2752                 }
2753                 g_free(outfile);
2754                 NEXT_PART_NOT_CHILD(child);
2755         }
2756         procmime_mimeinfo_free_all(mimeinfo);
2757 }
2758
2759 #undef NEXT_PART_NOT_CHILD
2760
2761
2762
2763 typedef enum {
2764         WAIT_FOR_INDENT_CHAR,
2765         WAIT_FOR_INDENT_CHAR_OR_SPACE,
2766 } IndentState;
2767
2768 /* return indent length, we allow:
2769    indent characters followed by indent characters or spaces/tabs,
2770    alphabets and numbers immediately followed by indent characters,
2771    and the repeating sequences of the above
2772    If quote ends with multiple spaces, only the first one is included. */
2773 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
2774                                     const GtkTextIter *start, gint *len)
2775 {
2776         GtkTextIter iter = *start;
2777         gunichar wc;
2778         gchar ch[6];
2779         gint clen;
2780         IndentState state = WAIT_FOR_INDENT_CHAR;
2781         gboolean is_space;
2782         gboolean is_indent;
2783         gint alnum_count = 0;
2784         gint space_count = 0;
2785         gint quote_len = 0;
2786
2787         if (prefs_common.quote_chars == NULL) {
2788                 return 0 ;
2789         }
2790
2791         while (!gtk_text_iter_ends_line(&iter)) {
2792                 wc = gtk_text_iter_get_char(&iter);
2793                 if (g_unichar_iswide(wc))
2794                         break;
2795                 clen = g_unichar_to_utf8(wc, ch);
2796                 if (clen != 1)
2797                         break;
2798
2799                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
2800                 is_space = g_unichar_isspace(wc);
2801
2802                 if (state == WAIT_FOR_INDENT_CHAR) {
2803                         if (!is_indent && !g_unichar_isalnum(wc))
2804                                 break;
2805                         if (is_indent) {
2806                                 quote_len += alnum_count + space_count + 1;
2807                                 alnum_count = space_count = 0;
2808                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2809                         } else
2810                                 alnum_count++;
2811                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
2812                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
2813                                 break;
2814                         if (is_space)
2815                                 space_count++;
2816                         else if (is_indent) {
2817                                 quote_len += alnum_count + space_count + 1;
2818                                 alnum_count = space_count = 0;
2819                         } else {
2820                                 alnum_count++;
2821                                 state = WAIT_FOR_INDENT_CHAR;
2822                         }
2823                 }
2824
2825                 gtk_text_iter_forward_char(&iter);
2826         }
2827
2828         if (quote_len > 0 && space_count > 0)
2829                 quote_len++;
2830
2831         if (len)
2832                 *len = quote_len;
2833
2834         if (quote_len > 0) {
2835                 iter = *start;
2836                 gtk_text_iter_forward_chars(&iter, quote_len);
2837                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
2838         }
2839
2840         return NULL;
2841 }
2842
2843 /* return TRUE if the line is itemized */
2844 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
2845                                     const GtkTextIter *start)
2846 {
2847         GtkTextIter iter = *start;
2848         gunichar wc;
2849         gchar ch[6];
2850         gint clen;
2851
2852         if (gtk_text_iter_ends_line(&iter))
2853                 return FALSE;
2854
2855         while (1) {
2856                 wc = gtk_text_iter_get_char(&iter);
2857                 if (!g_unichar_isspace(wc))
2858                         break;
2859                 gtk_text_iter_forward_char(&iter);
2860                 if (gtk_text_iter_ends_line(&iter))
2861                         return FALSE;
2862         }
2863
2864         clen = g_unichar_to_utf8(wc, ch);
2865         if (clen != 1)
2866                 return FALSE;
2867
2868         if (!strchr("*-+", ch[0]))
2869                 return FALSE;
2870
2871         gtk_text_iter_forward_char(&iter);
2872         if (gtk_text_iter_ends_line(&iter))
2873                 return FALSE;
2874         wc = gtk_text_iter_get_char(&iter);
2875         if (g_unichar_isspace(wc))
2876                 return TRUE;
2877
2878         return FALSE;
2879 }
2880
2881 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
2882                                            const GtkTextIter *start,
2883                                            GtkTextIter *break_pos,
2884                                            gint max_col,
2885                                            gint quote_len)
2886 {
2887         GtkTextIter iter = *start, line_end = *start;
2888         PangoLogAttr *attrs;
2889         gchar *str;
2890         gchar *p;
2891         gint len;
2892         gint i;
2893         gint col = 0;
2894         gint pos = 0;
2895         gboolean can_break = FALSE;
2896         gboolean do_break = FALSE;
2897         gboolean was_white = FALSE;
2898         gboolean prev_dont_break = FALSE;
2899
2900         gtk_text_iter_forward_to_line_end(&line_end);
2901         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
2902         len = g_utf8_strlen(str, -1);
2903         /* g_print("breaking line: %d: %s (len = %d)\n",
2904                 gtk_text_iter_get_line(&iter), str, len); */
2905         attrs = g_new(PangoLogAttr, len + 1);
2906
2907         pango_default_break(str, -1, NULL, attrs, len + 1);
2908
2909         p = str;
2910
2911         /* skip quote and leading spaces */
2912         for (i = 0; *p != '\0' && i < len; i++) {
2913                 gunichar wc;
2914
2915                 wc = g_utf8_get_char(p);
2916                 if (i >= quote_len && !g_unichar_isspace(wc))
2917                         break;
2918                 if (g_unichar_iswide(wc))
2919                         col += 2;
2920                 else if (*p == '\t')
2921                         col += 8;
2922                 else
2923                         col++;
2924                 p = g_utf8_next_char(p);
2925         }
2926
2927         for (; *p != '\0' && i < len; i++) {
2928                 PangoLogAttr *attr = attrs + i;
2929                 gunichar wc;
2930                 gint uri_len;
2931
2932                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
2933                         pos = i;
2934                 
2935                 was_white = attr->is_white;
2936
2937                 /* don't wrap URI */
2938                 if ((uri_len = get_uri_len(p)) > 0) {
2939                         col += uri_len;
2940                         if (pos > 0 && col > max_col) {
2941                                 do_break = TRUE;
2942                                 break;
2943                         }
2944                         i += uri_len - 1;
2945                         p += uri_len;
2946                         can_break = TRUE;
2947                         continue;
2948                 }
2949
2950                 wc = g_utf8_get_char(p);
2951                 if (g_unichar_iswide(wc)) {
2952                         col += 2;
2953                         if (prev_dont_break && can_break && attr->is_line_break)
2954                                 pos = i;
2955                 } else if (*p == '\t')
2956                         col += 8;
2957                 else
2958                         col++;
2959                 if (pos > 0 && col > max_col) {
2960                         do_break = TRUE;
2961                         break;
2962                 }
2963
2964                 if (*p == '-' || *p == '/')
2965                         prev_dont_break = TRUE;
2966                 else
2967                         prev_dont_break = FALSE;
2968
2969                 p = g_utf8_next_char(p);
2970                 can_break = TRUE;
2971         }
2972
2973         debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
2974
2975         g_free(attrs);
2976         g_free(str);
2977
2978         *break_pos = *start;
2979         gtk_text_iter_set_line_offset(break_pos, pos);
2980
2981         return do_break;
2982 }
2983
2984 static gboolean compose_join_next_line(Compose *compose,
2985                                        GtkTextBuffer *buffer,
2986                                        GtkTextIter *iter,
2987                                        const gchar *quote_str)
2988 {
2989         GtkTextIter iter_ = *iter, cur, prev, next, end;
2990         PangoLogAttr attrs[3];
2991         gchar *str;
2992         gchar *next_quote_str;
2993         gunichar wc1, wc2;
2994         gint quote_len;
2995         gboolean keep_cursor = FALSE;
2996
2997         if (!gtk_text_iter_forward_line(&iter_) ||
2998             gtk_text_iter_ends_line(&iter_))
2999                 return FALSE;
3000
3001         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
3002
3003         if ((quote_str || next_quote_str) &&
3004             strcmp2(quote_str, next_quote_str) != 0) {
3005                 g_free(next_quote_str);
3006                 return FALSE;
3007         }
3008         g_free(next_quote_str);
3009
3010         end = iter_;
3011         if (quote_len > 0) {
3012                 gtk_text_iter_forward_chars(&end, quote_len);
3013                 if (gtk_text_iter_ends_line(&end))
3014                         return FALSE;
3015         }
3016
3017         /* don't join itemized lines */
3018         if (compose_is_itemized(buffer, &end))
3019                 return FALSE;
3020
3021         /* don't join signature separator */
3022         if (compose_is_sig_separator(compose, buffer, &iter_))
3023                 return FALSE;
3024
3025         /* delete quote str */
3026         if (quote_len > 0)
3027                 gtk_text_buffer_delete(buffer, &iter_, &end);
3028
3029         /* delete linebreak and extra spaces */
3030         prev = cur = iter_;
3031         while (gtk_text_iter_backward_char(&cur)) {
3032                 wc1 = gtk_text_iter_get_char(&cur);
3033                 if (!g_unichar_isspace(wc1))
3034                         break;
3035                 prev = cur;
3036         }
3037         next = cur = iter_;
3038         while (!gtk_text_iter_ends_line(&cur)) {
3039                 wc1 = gtk_text_iter_get_char(&cur);
3040                 if (!g_unichar_isspace(wc1))
3041                         break;
3042                 gtk_text_iter_forward_char(&cur);
3043                 next = cur;
3044         }
3045         if (!gtk_text_iter_equal(&prev, &next)) {
3046                 GtkTextMark *mark;
3047
3048                 mark = gtk_text_buffer_get_insert(buffer);
3049                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3050                 if (gtk_text_iter_equal(&prev, &cur))
3051                         keep_cursor = TRUE;
3052                 gtk_text_buffer_delete(buffer, &prev, &next);
3053         }
3054         iter_ = prev;
3055
3056         /* insert space if required */
3057         gtk_text_iter_backward_char(&prev);
3058         wc1 = gtk_text_iter_get_char(&prev);
3059         wc2 = gtk_text_iter_get_char(&next);
3060         gtk_text_iter_forward_char(&next);
3061         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3062         pango_default_break(str, -1, NULL, attrs, 3);
3063         if (!attrs[1].is_line_break ||
3064             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3065                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3066                 if (keep_cursor) {
3067                         gtk_text_iter_backward_char(&iter_);
3068                         gtk_text_buffer_place_cursor(buffer, &iter_);
3069                 }
3070         }
3071         g_free(str);
3072
3073         *iter = iter_;
3074         return TRUE;
3075 }
3076
3077 #define ADD_TXT_POS(bp_, ep_, pti_) \
3078         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3079                 last = last->next; \
3080                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3081                 last->next = NULL; \
3082         } else { \
3083                 g_warning("alloc error scanning URIs\n"); \
3084         }
3085
3086 static void compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3087 {
3088         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3089         GtkTextBuffer *buffer;
3090         GtkTextIter iter, break_pos, end_of_line;
3091         gchar *quote_str = NULL;
3092         gint quote_len;
3093         gboolean wrap_quote = prefs_common.linewrap_quote;
3094         gboolean prev_autowrap = compose->autowrap;
3095         gint startq_offset = -1, noq_offset = -1;
3096         gint uri_start = -1, uri_stop = -1;
3097         gint nouri_start = -1, nouri_stop = -1;
3098
3099         compose->autowrap = FALSE;
3100
3101         buffer = gtk_text_view_get_buffer(text);
3102
3103         undo_block(compose->undostruct);
3104         
3105         if (par_iter) {
3106                 iter = *par_iter;
3107         } else {
3108                 GtkTextMark *mark;
3109                 mark = gtk_text_buffer_get_insert(buffer);
3110                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3111         }
3112
3113         /* move to paragraph start */
3114         gtk_text_iter_set_line_offset(&iter, 0);
3115         if (gtk_text_iter_ends_line(&iter)) {
3116                 while (gtk_text_iter_ends_line(&iter) &&
3117                        gtk_text_iter_forward_line(&iter))
3118                         ;
3119         } else {
3120                 while (gtk_text_iter_backward_line(&iter)) {
3121                         if (gtk_text_iter_ends_line(&iter)) {
3122                                 gtk_text_iter_forward_line(&iter);
3123                                 break;
3124                         }
3125                 }
3126         }
3127
3128         /* go until paragraph end (empty line) */
3129         while (!gtk_text_iter_ends_line(&iter)) {
3130                 gchar *scanpos = NULL;
3131                 /* parse table - in order of priority */
3132                 struct table {
3133                         const gchar *needle; /* token */
3134
3135                         /* token search function */
3136                         gchar    *(*search)     (const gchar *haystack,
3137                                                  const gchar *needle);
3138                         /* part parsing function */
3139                         gboolean  (*parse)      (const gchar *start,
3140                                                  const gchar *scanpos,
3141                                                  const gchar **bp_,
3142                                                  const gchar **ep_);
3143                         /* part to URI function */
3144                         gchar    *(*build_uri)  (const gchar *bp,
3145                                                  const gchar *ep);
3146                 };
3147
3148                 static struct table parser[] = {
3149                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
3150                         {"https://", strcasestr, get_uri_part,   make_uri_string},
3151                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
3152                         {"www.",     strcasestr, get_uri_part,   make_http_string},
3153                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
3154                         {"@",        strcasestr, get_email_part, make_email_string}
3155                 };
3156                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
3157                 gint last_index = PARSE_ELEMS;
3158                 gint  n;
3159                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
3160                 gint walk_pos;
3161                 
3162                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
3163                         goto colorize;
3164
3165                 uri_start = uri_stop = -1;
3166                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
3167
3168                 if (quote_str) {
3169                         if (!wrap_quote) {
3170                                 if (startq_offset == -1) {
3171                                         startq_offset = gtk_text_iter_get_offset(&iter);
3172                                 }
3173                                 goto colorize;
3174                         }
3175                         debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
3176                         if (startq_offset == -1) 
3177                                 startq_offset = gtk_text_iter_get_offset(&iter);
3178                 } else {
3179                         if (startq_offset == -1)
3180                                 noq_offset = gtk_text_iter_get_offset(&iter);
3181                 }
3182
3183                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
3184                         goto colorize;
3185                 }
3186                 if (compose_get_line_break_pos(buffer, &iter, &break_pos,
3187                                                prefs_common.linewrap_len,
3188                                                quote_len)) {
3189                         GtkTextIter prev, next, cur;
3190                         
3191                         if (prev_autowrap != FALSE || force)
3192                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
3193                         else if (quote_str && wrap_quote)
3194                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
3195                         else 
3196                                 goto colorize;
3197                         /* remove trailing spaces */
3198                         cur = break_pos;
3199                         gtk_text_iter_backward_char(&cur);
3200                         prev = next = cur;
3201                         while (!gtk_text_iter_starts_line(&cur)) {
3202                                 gunichar wc;
3203
3204                                 gtk_text_iter_backward_char(&cur);
3205                                 wc = gtk_text_iter_get_char(&cur);
3206                                 if (!g_unichar_isspace(wc))
3207                                         break;
3208                                 prev = cur;
3209                         }
3210                         if (!gtk_text_iter_equal(&prev, &next)) {
3211                                 gtk_text_buffer_delete(buffer, &prev, &next);
3212                                 break_pos = next;
3213                                 gtk_text_iter_forward_char(&break_pos);
3214                         }
3215
3216                         if (quote_str)
3217                                 gtk_text_buffer_insert(buffer, &break_pos,
3218                                                        quote_str, -1);
3219
3220                         iter = break_pos;
3221                         compose_join_next_line(compose, buffer, &iter, quote_str);
3222
3223                         /* move iter to current line start */
3224                         gtk_text_iter_set_line_offset(&iter, 0);
3225                         continue;
3226                 } else {
3227                         /* move iter to next line start */
3228                         iter = break_pos;
3229                 }
3230
3231 colorize:
3232                 end_of_line = iter;
3233                 while (!gtk_text_iter_ends_line(&end_of_line)) {
3234                         gtk_text_iter_forward_char(&end_of_line);
3235                 }
3236                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
3237
3238                 nouri_start = gtk_text_iter_get_offset(&iter);
3239                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
3240
3241                 walk_pos = gtk_text_iter_get_offset(&iter);
3242                 /* FIXME: this looks phony. scanning for anything in the parse table */
3243                 for (n = 0; n < PARSE_ELEMS; n++) {
3244                         gchar *tmp;
3245
3246                         tmp = parser[n].search(walk, parser[n].needle);
3247                         if (tmp) {
3248                                 if (scanpos == NULL || tmp < scanpos) {
3249                                         scanpos = tmp;
3250                                         last_index = n;
3251                                 }
3252                         }                                       
3253                 }
3254
3255                 bp = ep = 0;
3256                 if (scanpos) {
3257                         /* check if URI can be parsed */
3258                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,(const gchar **)&ep)
3259                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
3260                                         walk = ep;
3261                         } else
3262                                 walk = scanpos +
3263                                         strlen(parser[last_index].needle);
3264                 } 
3265                 if (bp && ep) {
3266                         uri_start = walk_pos + (bp - o_walk);
3267                         uri_stop  = walk_pos + (ep - o_walk);
3268                 }
3269                 g_free(o_walk);
3270                 o_walk = NULL;
3271                 gtk_text_iter_forward_line(&iter);
3272                 g_free(quote_str);
3273                 quote_str = NULL;
3274                 if (startq_offset != -1) {
3275                         GtkTextIter startquote, endquote;
3276                         gtk_text_buffer_get_iter_at_offset(
3277                                 buffer, &startquote, startq_offset);
3278                         endquote = iter;
3279                         gtk_text_buffer_apply_tag_by_name(
3280                                 buffer, "quote", &startquote, &endquote);
3281                         startq_offset = -1;
3282                 } else if (noq_offset != -1) {
3283                         GtkTextIter startnoquote, endnoquote;
3284                         gtk_text_buffer_get_iter_at_offset(
3285                                 buffer, &startnoquote, noq_offset);
3286                         endnoquote = iter;
3287                         gtk_text_buffer_remove_tag_by_name(
3288                                 buffer, "quote", &startnoquote, &endnoquote);
3289                         noq_offset = -1;
3290                 }
3291                 
3292                 /* always */ {
3293                         GtkTextIter nouri_start_iter, nouri_end_iter;
3294                         gtk_text_buffer_get_iter_at_offset(
3295                                 buffer, &nouri_start_iter, nouri_start);
3296                         gtk_text_buffer_get_iter_at_offset(
3297                                 buffer, &nouri_end_iter, nouri_stop);
3298                         gtk_text_buffer_remove_tag_by_name(
3299                                 buffer, "link", &nouri_start_iter, &nouri_end_iter);
3300                 }
3301                 if (uri_start > 0 && uri_stop > 0) {
3302                         GtkTextIter uri_start_iter, uri_end_iter;
3303                         gtk_text_buffer_get_iter_at_offset(
3304                                 buffer, &uri_start_iter, uri_start);
3305                         gtk_text_buffer_get_iter_at_offset(
3306                                 buffer, &uri_end_iter, uri_stop);
3307                         gtk_text_buffer_apply_tag_by_name(
3308                                 buffer, "link", &uri_start_iter, &uri_end_iter);
3309                 }
3310         }
3311
3312         if (par_iter)
3313                 *par_iter = iter;
3314
3315         undo_unblock(compose->undostruct);
3316         compose->autowrap = prev_autowrap;
3317 }
3318
3319 static void compose_wrap_all(Compose *compose)
3320 {
3321         compose_wrap_all_full(compose, FALSE);
3322 }
3323
3324 static void compose_wrap_all_full(Compose *compose, gboolean force)
3325 {
3326         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3327         GtkTextBuffer *buffer;
3328         GtkTextIter iter;
3329
3330         buffer = gtk_text_view_get_buffer(text);
3331
3332         undo_block(compose->undostruct);
3333
3334         gtk_text_buffer_get_start_iter(buffer, &iter);
3335         while (!gtk_text_iter_is_end(&iter))
3336                 compose_beautify_paragraph(compose, &iter, force);
3337
3338         undo_unblock(compose->undostruct);
3339 }
3340
3341 static void compose_set_title(Compose *compose)
3342 {
3343         gchar *str;
3344         gchar *edited;
3345         gchar *subject;
3346         
3347         edited = compose->modified ? _(" [Edited]") : "";
3348         
3349         subject = gtk_editable_get_chars(
3350                         GTK_EDITABLE(compose->subject_entry), 0, -1);
3351
3352         if (subject && strlen(subject))
3353                 str = g_strdup_printf(_("%s - Compose message%s"),
3354                                       subject, edited); 
3355         else if (compose->account && compose->account->address)
3356                 str = g_strdup_printf(_("%s - Compose message%s"),
3357                                       compose->account->address, edited);
3358         else
3359                 str = g_strdup_printf(_("Compose message%s"), edited);
3360         gtk_window_set_title(GTK_WINDOW(compose->window), str);
3361         g_free(str);
3362         g_free(subject);
3363 }
3364
3365 /**
3366  * compose_current_mail_account:
3367  * 
3368  * Find a current mail account (the currently selected account, or the
3369  * default account, if a news account is currently selected).  If a
3370  * mail account cannot be found, display an error message.
3371  * 
3372  * Return value: Mail account, or NULL if not found.
3373  **/
3374 static PrefsAccount *
3375 compose_current_mail_account(void)
3376 {
3377         PrefsAccount *ac;
3378
3379         if (cur_account && cur_account->protocol != A_NNTP)
3380                 ac = cur_account;
3381         else {
3382                 ac = account_get_default();
3383                 if (!ac || ac->protocol == A_NNTP) {
3384                         alertpanel_error(_("Account for sending mail is not specified.\n"
3385                                            "Please select a mail account before sending."));
3386                         return NULL;
3387                 }
3388         }
3389         return ac;
3390 }
3391
3392 static void compose_select_account(Compose *compose, PrefsAccount *account,
3393                                    gboolean init)
3394 {
3395         GtkItemFactory *ifactory;
3396
3397         g_return_if_fail(account != NULL);
3398
3399         compose->account = account;
3400
3401         compose_set_title(compose);
3402
3403         ifactory = gtk_item_factory_from_widget(compose->menubar);
3404
3405         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
3406                 menu_set_active(ifactory, "/Options/Sign", TRUE);
3407         else
3408                 menu_set_active(ifactory, "/Options/Sign", FALSE);
3409         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
3410                 menu_set_active(ifactory, "/Options/Encrypt", TRUE);
3411         else
3412                 menu_set_active(ifactory, "/Options/Encrypt", FALSE);
3413                                        
3414         activate_privacy_system(compose, account, FALSE);
3415
3416         if (!init && compose->mode != COMPOSE_REDIRECT)
3417                 compose_insert_sig(compose, TRUE);
3418 }
3419
3420 gboolean compose_check_for_valid_recipient(Compose *compose) {
3421         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
3422         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
3423         gboolean recipient_found = FALSE;
3424         GSList *list;
3425         gchar **strptr;
3426
3427         /* free to and newsgroup list */
3428         slist_free_strings(compose->to_list);
3429         g_slist_free(compose->to_list);
3430         compose->to_list = NULL;
3431                         
3432         slist_free_strings(compose->newsgroup_list);
3433         g_slist_free(compose->newsgroup_list);
3434         compose->newsgroup_list = NULL;
3435
3436         /* search header entries for to and newsgroup entries */
3437         for (list = compose->header_list; list; list = list->next) {
3438                 gchar *header;
3439                 gchar *entry;
3440                 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
3441                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
3442                 g_strstrip(entry);
3443                 if (entry[0] != '\0') {
3444                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
3445                                 if (!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
3446                                         compose->to_list = address_list_append(compose->to_list, entry);
3447                                         recipient_found = TRUE;
3448                                 }
3449                         }
3450                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
3451                                 if (!strcmp(header, (prefs_common.trans_hdr ? gettext(*strptr) : *strptr))) {
3452                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
3453                                         recipient_found = TRUE;
3454                                 }
3455                         }
3456                 }
3457                 g_free(header);
3458                 g_free(entry);
3459         }
3460         return recipient_found;
3461 }
3462
3463 static gboolean compose_check_entries(Compose *compose, gboolean check_subject)
3464 {
3465         const gchar *str;
3466
3467         if (compose_check_for_valid_recipient(compose) == FALSE) {
3468                 alertpanel_error(_("Recipient is not specified."));
3469                 return FALSE;
3470         }
3471
3472         str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3473         if (*str == '\0' && check_subject == TRUE) {
3474                 AlertValue aval;
3475
3476                 aval = alertpanel(_("Send"),
3477                                   _("Subject is empty. Send it anyway?"),
3478                                   GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3479                 if (aval != G_ALERTDEFAULT)
3480                         return FALSE;
3481         }
3482
3483         return TRUE;
3484 }
3485
3486 gint compose_send(Compose *compose)
3487 {
3488         gint msgnum;
3489         FolderItem *folder;
3490         gint val = -1;
3491         gchar *msgpath;
3492
3493         compose_allow_user_actions (compose, FALSE);
3494         compose->sending = TRUE;
3495
3496         if (compose_check_entries(compose, TRUE) == FALSE)
3497                 goto bail;
3498
3499         val = compose_queue(compose, &msgnum, &folder);
3500
3501         if (val) {
3502                 if (val == -4) {
3503                         alertpanel_error(_("Could not queue message for sending:\n\n"
3504                                            "Charset conversion failed."));
3505                 } else if (val == -3) {
3506                         alertpanel_error(_("Could not queue message for sending:\n\n"
3507                                            "Signature failed."));
3508                 } else if (val == -2) {
3509                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
3510                 } else {
3511                         alertpanel_error(_("Could not queue message for sending."));
3512                 }
3513                 goto bail;
3514         }
3515
3516
3517         if (prefs_common.send_dialog_mode != SEND_DIALOG_ALWAYS) {
3518                 compose->sending = FALSE;
3519                 compose_close(compose);
3520                 /* No more compose access in the normal codepath 
3521                  * after this point! */
3522                 compose = NULL;
3523         }
3524
3525         if (msgnum == 0) {
3526                 alertpanel_error(_("The message was queued but could not be "
3527                                    "sent.\nUse \"Send queued messages\" from "
3528                                    "the main window to retry."));
3529                 if (prefs_common.send_dialog_mode == SEND_DIALOG_ALWAYS) {
3530                         compose->sending = FALSE;
3531                         compose_allow_user_actions (compose, TRUE);
3532                 }
3533                 return 0;
3534         }
3535         
3536         msgpath = folder_item_fetch_msg(folder, msgnum);
3537         val = procmsg_send_message_queue(msgpath);
3538         g_free(msgpath);
3539
3540         if (prefs_common.send_dialog_mode == SEND_DIALOG_ALWAYS) {
3541                 compose->sending = FALSE;
3542                 compose_allow_user_actions (compose, TRUE);
3543                 if (val != 0) {
3544                         folder_item_remove_msg(folder, msgnum);
3545                         folder_item_scan(folder);
3546                 }
3547         }
3548
3549         if (val == 0) {
3550                 folder_item_remove_msg(folder, msgnum);
3551                 folder_item_scan(folder);
3552                 if (prefs_common.send_dialog_mode == SEND_DIALOG_ALWAYS)
3553                         compose_close(compose);
3554         } else {
3555                 alertpanel_error(_("The message was queued but could not be "
3556                                    "sent.\nUse \"Send queued messages\" from "
3557                                    "the main window to retry."));
3558                 if (prefs_common.send_dialog_mode == SEND_DIALOG_ALWAYS) {
3559                         compose_allow_user_actions (compose, TRUE);
3560                         compose->sending = FALSE;               
3561                 }
3562                 return -1;
3563         }
3564
3565         return 0;
3566
3567 bail:
3568         compose_allow_user_actions (compose, TRUE);
3569         compose->sending = FALSE;
3570
3571         return -1;
3572 }
3573
3574 static gboolean compose_use_attach(Compose *compose) 
3575 {
3576         GtkTreeModel *model = gtk_tree_view_get_model
3577                                 (GTK_TREE_VIEW(compose->attach_clist));
3578         return gtk_tree_model_iter_n_children(model, NULL) > 0;
3579 }
3580
3581 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
3582                                                            FILE *fp)
3583 {
3584         gchar buf[BUFFSIZE];
3585         gchar *str;
3586         gboolean first_to_address;
3587         gboolean first_cc_address;
3588         GSList *list;
3589         ComposeHeaderEntry *headerentry;
3590         const gchar *headerentryname;
3591         gchar *cc_hdr;
3592         gchar *to_hdr;
3593
3594         debug_print("Writing redirect header\n");
3595
3596         cc_hdr = prefs_common.trans_hdr ? _("Cc:") : "Cc:";
3597         to_hdr = prefs_common.trans_hdr ? _("To:") : "To:";
3598
3599         first_to_address = TRUE;
3600         for (list = compose->header_list; list; list = list->next) {
3601                 headerentry = ((ComposeHeaderEntry *)list->data);
3602                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
3603
3604                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
3605                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
3606                         Xstrdup_a(str, entstr, return -1);
3607                         g_strstrip(str);
3608                         if (str[0] != '\0') {
3609                                 compose_convert_header
3610                                         (compose, buf, sizeof(buf), str,
3611                                         strlen("Resent-To") + 2, TRUE);
3612
3613                                 if (first_to_address) {
3614                                         fprintf(fp, "Resent-To: ");
3615                                         first_to_address = FALSE;
3616                                 } else {
3617                                         fprintf(fp, ",");
3618                                 }
3619                                 fprintf(fp, "%s", buf);
3620                         }
3621                 }
3622         }
3623         if (!first_to_address) {
3624                 fprintf(fp, "\n");
3625         }
3626
3627         first_cc_address = TRUE;
3628         for (list = compose->header_list; list; list = list->next) {
3629                 headerentry = ((ComposeHeaderEntry *)list->data);
3630                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
3631
3632                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
3633                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
3634                         Xstrdup_a(str, strg, return -1);
3635                         g_strstrip(str);
3636                         if (str[0] != '\0') {
3637                                 compose_convert_header
3638                                         (compose, buf, sizeof(buf), str,
3639                                         strlen("Resent-Cc") + 2, TRUE);
3640
3641                                 if (first_cc_address) {
3642                                         fprintf(fp, "Resent-Cc: ");
3643                                         first_cc_address = FALSE;
3644                                 } else {
3645                                         fprintf(fp, ",");
3646                                 }
3647                                 fprintf(fp, "%s", buf);
3648                         }
3649                 }
3650         }
3651         if (!first_cc_address) {
3652                 fprintf(fp, "\n");
3653         }
3654         
3655         return(0);
3656 }
3657
3658 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
3659 {
3660         gchar buf[BUFFSIZE];
3661         gchar *str;
3662         const gchar *entstr;
3663         /* struct utsname utsbuf; */
3664
3665         g_return_val_if_fail(fp != NULL, -1);
3666         g_return_val_if_fail(compose->account != NULL, -1);
3667         g_return_val_if_fail(compose->account->address != NULL, -1);
3668
3669         /* Resent-Date */
3670         get_rfc822_date(buf, sizeof(buf));
3671         fprintf(fp, "Resent-Date: %s\n", buf);
3672
3673         /* Resent-From */
3674         if (compose->account->name && *compose->account->name) {
3675                 compose_convert_header
3676                         (compose, buf, sizeof(buf), compose->account->name,
3677                          strlen("From: "), TRUE);
3678                 fprintf(fp, "Resent-From: %s <%s>\n",
3679                         buf, compose->account->address);
3680         } else
3681                 fprintf(fp, "Resent-From: %s\n", compose->account->address);
3682
3683         /* Subject */
3684         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3685         if (*entstr != '\0') {
3686                 Xstrdup_a(str, entstr, return -1);
3687                 g_strstrip(str);
3688                 if (*str != '\0') {
3689                         compose_convert_header(compose, buf, sizeof(buf), str,
3690                                                strlen("Subject: "), FALSE);
3691                         fprintf(fp, "Subject: %s\n", buf);
3692                 }
3693         }
3694
3695         /* Resent-Message-ID */
3696         if (compose->account->gen_msgid) {
3697                 generate_msgid(buf, sizeof(buf));
3698                 fprintf(fp, "Resent-Message-ID: <%s>\n", buf);
3699                 compose->msgid = g_strdup(buf);
3700         }
3701
3702         compose_redirect_write_headers_from_headerlist(compose, fp);
3703
3704         /* separator between header and body */
3705         fputs("\n", fp);
3706
3707         return 0;
3708 }
3709
3710 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
3711 {
3712         FILE *fp;
3713         size_t len;
3714         gchar buf[BUFFSIZE];
3715
3716         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
3717                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
3718                 return -1;
3719         }
3720
3721         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
3722                 /* should filter returnpath, delivered-to */
3723                 if (g_ascii_strncasecmp(buf, "Return-Path:",
3724                                         strlen("Return-Path:")) == 0 ||
3725                     g_ascii_strncasecmp(buf, "Delivered-To:",
3726                                         strlen("Delivered-To:")) == 0 ||
3727                     g_ascii_strncasecmp(buf, "Received:",
3728                                         strlen("Received:")) == 0 ||
3729                     g_ascii_strncasecmp(buf, "Subject:",
3730                                         strlen("Subject:")) == 0 ||
3731                     g_ascii_strncasecmp(buf, "X-UIDL:",
3732                                         strlen("X-UIDL:")) == 0)
3733                         continue;
3734
3735                 if (fputs(buf, fdest) == -1)
3736                         goto error;
3737
3738                 if (!prefs_common.redirect_keep_from) {
3739                         if (g_ascii_strncasecmp(buf, "From:",
3740                                           strlen("From:")) == 0) {
3741                                 fputs(" (by way of ", fdest);
3742                                 if (compose->account->name
3743                                     && *compose->account->name) {
3744                                         compose_convert_header
3745                                                 (compose, buf, sizeof(buf),
3746                                                  compose->account->name,
3747                                                  strlen("From: "),
3748                                                  FALSE);
3749                                         fprintf(fdest, "%s <%s>",
3750                                                 buf,
3751                                                 compose->account->address);
3752                                 } else
3753                                         fprintf(fdest, "%s",
3754                                                 compose->account->address);
3755                                 fputs(")", fdest);
3756                         }
3757                 }
3758
3759                 if (fputs("\n", fdest) == -1)
3760                         goto error;
3761         }
3762
3763         compose_redirect_write_headers(compose, fdest);
3764
3765         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
3766                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
3767                         goto error;
3768         }
3769
3770         fclose(fp);
3771
3772         return 0;
3773 error:
3774         fclose(fp);
3775
3776         return -1;
3777 }
3778
3779 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action)
3780 {
3781         GtkTextBuffer *buffer;
3782         GtkTextIter start, end;
3783         gchar *chars;
3784         gchar *buf;
3785         const gchar *out_codeset;
3786         EncodingType encoding;
3787         MimeInfo *mimemsg, *mimetext;
3788         gint line;
3789
3790         /* create message MimeInfo */
3791         mimemsg = procmime_mimeinfo_new();
3792         mimemsg->type = MIMETYPE_MESSAGE;
3793         mimemsg->subtype = g_strdup("rfc822");
3794         mimemsg->content = MIMECONTENT_MEM;
3795         mimemsg->data.mem = compose_get_header(compose);
3796
3797         /* Create text part MimeInfo */
3798         /* get all composed text */
3799         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3800         gtk_text_buffer_get_start_iter(buffer, &start);
3801         gtk_text_buffer_get_end_iter(buffer, &end);
3802         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3803         if (is_ascii_str(chars)) {
3804                 buf = chars;
3805                 chars = NULL;
3806                 out_codeset = CS_US_ASCII;
3807                 encoding = ENC_7BIT;
3808         } else {
3809                 const gchar *src_codeset = CS_INTERNAL;
3810
3811                 out_codeset = conv_get_charset_str(compose->out_encoding);
3812
3813                 if (!out_codeset) {
3814                         gchar *test_conv_global_out = NULL;
3815                         gchar *test_conv_reply = NULL;
3816
3817                         /* automatic mode. be automatic. */
3818                         codeconv_set_strict(TRUE);
3819                         
3820                         out_codeset = conv_get_outgoing_charset_str();
3821                         if (out_codeset) {
3822                                 debug_print("trying to convert to %s\n", out_codeset);
3823                                 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
3824                         }
3825                         
3826                         if (!test_conv_global_out && compose->orig_charset) {
3827                                 out_codeset = compose->orig_charset;
3828                                 debug_print("failure; trying to convert to %s\n", out_codeset);
3829                                 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
3830                         }
3831                         
3832                         if (!test_conv_global_out && !test_conv_reply) {
3833                                 /* we're lost */
3834                                 out_codeset = CS_INTERNAL;
3835                                 debug_print("finally using %s\n", out_codeset);
3836                         }
3837                         g_free(test_conv_global_out);
3838                         g_free(test_conv_reply);
3839                         codeconv_set_strict(FALSE);
3840                 }
3841
3842                 if (!g_ascii_strcasecmp(out_codeset, CS_US_ASCII))
3843                         out_codeset = CS_ISO_8859_1;
3844
3845                 if (prefs_common.encoding_method == CTE_BASE64)
3846                         encoding = ENC_BASE64;
3847                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
3848                         encoding = ENC_QUOTED_PRINTABLE;
3849                 else if (prefs_common.encoding_method == CTE_8BIT)
3850                         encoding = ENC_8BIT;
3851                 else
3852                         encoding = procmime_get_encoding_for_charset(out_codeset);
3853
3854                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
3855                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
3856
3857                 if (action == COMPOSE_WRITE_FOR_SEND) {
3858                         codeconv_set_strict(TRUE);
3859                         buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
3860                         codeconv_set_strict(FALSE);
3861
3862                         if (!buf) {
3863                                 AlertValue aval;
3864                                 gchar *msg;
3865
3866                                 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
3867                                                         "to the specified %s charset.\n"
3868                                                         "Send it as %s?"), out_codeset, src_codeset);
3869                                 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_YES, GTK_STOCK_NO, NULL, FALSE,
3870                                                       NULL, ALERT_ERROR, G_ALERTALTERNATE);
3871                                 g_free(msg);
3872
3873                                 if (aval != G_ALERTDEFAULT) {
3874                                         g_free(chars);
3875                                         return -3;
3876                                 } else {
3877                                         buf = chars;
3878                                         out_codeset = src_codeset;
3879                                         chars = NULL;
3880                                 }
3881                         }
3882                 } else {
3883                         buf = chars;
3884                         out_codeset = src_codeset;
3885                         chars = NULL;
3886                 }
3887         }
3888         g_free(chars);
3889
3890         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
3891                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
3892                     strstr(buf, "\nFrom ") != NULL) {
3893                         encoding = ENC_QUOTED_PRINTABLE;
3894                 }
3895         }
3896
3897         mimetext = procmime_mimeinfo_new();
3898         mimetext->content = MIMECONTENT_MEM;
3899         mimetext->data.mem = buf;
3900         mimetext->type = MIMETYPE_TEXT;
3901         mimetext->subtype = g_strdup("plain");
3902         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
3903                             g_strdup(out_codeset));
3904         /* protect trailing spaces when signing message */
3905         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
3906             privacy_system_can_sign(compose->privacy_system)) {
3907                 if (encoding == ENC_7BIT)
3908                         encoding = ENC_QUOTED_PRINTABLE;
3909                 else if (encoding == ENC_8BIT)
3910                         encoding = ENC_BASE64;
3911         }
3912         
3913         /* check for line length limit */
3914         if (action == COMPOSE_WRITE_FOR_SEND &&
3915             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
3916             check_line_length(buf, 1000, &line) < 0) {
3917                 AlertValue aval;
3918                 gchar *msg;
3919
3920                 msg = g_strdup_printf
3921                         (_("Line %d exceeds the line length limit (998 bytes).\n"
3922                            "The contents of the message might be broken on the way to the delivery.\n"
3923                            "\n"
3924                            "Send it anyway?"), line + 1);
3925                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
3926                 g_free(msg);
3927                 if (aval != G_ALERTDEFAULT) {
3928                         return -1;
3929                 }
3930         }
3931         
3932         if (encoding != ENC_UNKNOWN)
3933                 procmime_encode_content(mimetext, encoding);
3934
3935         /* append attachment parts */
3936         if (compose_use_attach(compose)) {
3937                 MimeInfo *mimempart;
3938
3939                 mimempart = procmime_mimeinfo_new();
3940                 mimempart->content = MIMECONTENT_EMPTY;
3941                 mimempart->type = MIMETYPE_MULTIPART;
3942                 mimempart->subtype = g_strdup("mixed");
3943                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
3944                                     generate_mime_boundary(NULL));
3945
3946                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
3947
3948                 g_node_append(mimempart->node, mimetext->node);
3949                 g_node_append(mimemsg->node, mimempart->node);
3950
3951                 compose_add_attachments(compose, mimempart);
3952         } else
3953                 g_node_append(mimemsg->node, mimetext->node);
3954
3955         /* sign message if sending */
3956         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
3957             privacy_system_can_sign(compose->privacy_system))
3958                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
3959                         return -2;
3960
3961         procmime_write_mimeinfo(mimemsg, fp);
3962         
3963         procmime_mimeinfo_free_all(mimemsg);
3964
3965         return 0;
3966 }
3967
3968 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
3969 {
3970         GtkTextBuffer *buffer;
3971         GtkTextIter start, end;
3972         FILE *fp;
3973         size_t len;
3974         gchar *chars, *tmp;
3975
3976         if ((fp = g_fopen(file, "wb")) == NULL) {
3977                 FILE_OP_ERROR(file, "fopen");
3978                 return -1;
3979         }
3980
3981         /* chmod for security */
3982         if (change_file_mode_rw(fp, file) < 0) {
3983                 FILE_OP_ERROR(file, "chmod");
3984                 g_warning("can't change file mode\n");
3985         }
3986
3987         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3988         gtk_text_buffer_get_start_iter(buffer, &start);
3989         gtk_text_buffer_get_end_iter(buffer, &end);
3990         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3991
3992         chars = conv_codeset_strdup
3993                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
3994
3995         g_free(tmp);
3996         if (!chars) return -1;
3997
3998         /* write body */
3999         len = strlen(chars);
4000         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
4001                 FILE_OP_ERROR(file, "fwrite");
4002                 g_free(chars);
4003                 fclose(fp);
4004                 g_unlink(file);
4005                 return -1;
4006         }
4007
4008         g_free(chars);
4009
4010         if (fclose(fp) == EOF) {
4011                 FILE_OP_ERROR(file, "fclose");
4012                 g_unlink(file);
4013                 return -1;
4014         }
4015         return 0;
4016 }
4017
4018 static gint compose_remove_reedit_target(Compose *compose)
4019 {
4020         FolderItem *item;
4021         MsgInfo *msginfo = compose->targetinfo;
4022
4023         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
4024         if (!msginfo) return -1;
4025
4026         item = msginfo->folder;
4027         g_return_val_if_fail(item != NULL, -1);
4028
4029         if (procmsg_msg_exist(msginfo) &&
4030             (folder_has_parent_of_type(item, F_QUEUE) ||
4031              folder_has_parent_of_type(item, F_DRAFT) 
4032              || msginfo == compose->autosaved_draft)) {
4033                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
4034                         g_warning("can't remove the old message\n");
4035                         return -1;
4036                 }
4037         }
4038
4039         return 0;
4040 }
4041
4042 void compose_remove_draft(Compose *compose)
4043 {
4044         FolderItem *drafts;
4045         MsgInfo *msginfo = compose->targetinfo;
4046         drafts = account_get_special_folder(compose->account, F_DRAFT);
4047
4048         if (procmsg_msg_exist(msginfo)) {
4049                 folder_item_remove_msg(drafts, msginfo->msgnum);
4050         }
4051
4052 }
4053
4054 static gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item)
4055 {
4056         return compose_queue_sub (compose, msgnum, item, FALSE);
4057 }
4058 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, gboolean check_subject)
4059 {
4060         FolderItem *queue;
4061         gchar *tmp;
4062         FILE *fp;
4063         GSList *cur;
4064         gint num;
4065         static gboolean lock = FALSE;
4066         PrefsAccount *mailac = NULL, *newsac = NULL;
4067         
4068         debug_print("queueing message...\n");
4069         g_return_val_if_fail(compose->account != NULL, -1);
4070
4071         lock = TRUE;
4072         
4073         if (compose_check_entries(compose, check_subject) == FALSE) {
4074                 lock = FALSE;
4075                 return -1;
4076         }
4077
4078         if (!compose->to_list && !compose->newsgroup_list) {
4079                 g_warning("can't get recipient list.");
4080                 lock = FALSE;
4081                 return -1;
4082         }
4083
4084         if (compose->to_list) {
4085                 if (compose->account->protocol != A_NNTP)
4086                         mailac = compose->account;
4087                 else if (cur_account && cur_account->protocol != A_NNTP)
4088                         mailac = cur_account;
4089                 else if (!(mailac = compose_current_mail_account())) {
4090                         lock = FALSE;
4091                         alertpanel_error(_("No account for sending mails available!"));
4092                         return -1;
4093                 }
4094         }
4095
4096         if (compose->newsgroup_list) {
4097                 if (compose->account->protocol == A_NNTP)
4098                         newsac = compose->account;
4099                 else if (!newsac->protocol != A_NNTP) {
4100                         lock = FALSE;
4101                         alertpanel_error(_("No account for posting news available!"));
4102                         return -1;
4103                 }                       
4104         }
4105
4106         if (prefs_common.linewrap_at_send)
4107                 compose_wrap_all(compose);
4108
4109         /* write queue header */
4110         tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
4111                               G_DIR_SEPARATOR, compose);
4112         if ((fp = g_fopen(tmp, "wb")) == NULL) {
4113                 FILE_OP_ERROR(tmp, "fopen");
4114                 g_free(tmp);
4115                 return -2;
4116         }
4117
4118         if (change_file_mode_rw(fp, tmp) < 0) {
4119                 FILE_OP_ERROR(tmp, "chmod");
4120                 g_warning("can't change file mode\n");
4121         }
4122
4123         /* queueing variables */
4124         fprintf(fp, "AF:\n");
4125         fprintf(fp, "NF:0\n");
4126         fprintf(fp, "PS:10\n");
4127         fprintf(fp, "SRH:1\n");
4128         fprintf(fp, "SFN:\n");
4129         fprintf(fp, "DSR:\n");
4130         if (compose->msgid)
4131                 fprintf(fp, "MID:<%s>\n", compose->msgid);
4132         else
4133                 fprintf(fp, "MID:\n");
4134         fprintf(fp, "CFG:\n");
4135         fprintf(fp, "PT:0\n");
4136         fprintf(fp, "S:%s\n", compose->account->address);
4137         fprintf(fp, "RQ:\n");
4138         if (mailac)
4139                 fprintf(fp, "SSV:%s\n", mailac->smtp_server);
4140         else
4141                 fprintf(fp, "SSV:\n");
4142         if (newsac)
4143                 fprintf(fp, "NSV:%s\n", newsac->nntp_server);
4144         else
4145                 fprintf(fp, "NSV:\n");
4146         fprintf(fp, "SSH:\n");
4147         /* write recepient list */
4148         if (compose->to_list) {
4149                 fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
4150                 for (cur = compose->to_list->next; cur != NULL;
4151                      cur = cur->next)
4152                         fprintf(fp, ",<%s>", (gchar *)cur->data);
4153                 fprintf(fp, "\n");
4154         }
4155         /* write newsgroup list */
4156         if (compose->newsgroup_list) {
4157                 fprintf(fp, "NG:");
4158                 fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data);
4159                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
4160                         fprintf(fp, ",%s", (gchar *)cur->data);
4161                 fprintf(fp, "\n");
4162         }
4163         /* Sylpheed account IDs */
4164         if (mailac)
4165                 fprintf(fp, "MAID:%d\n", mailac->account_id);
4166         if (newsac)
4167                 fprintf(fp, "NAID:%d\n", newsac->account_id);
4168
4169         if (compose->privacy_system != NULL) {
4170                 fprintf(fp, "X-Sylpheed-Privacy-System:%s\n", compose->privacy_system);
4171                 fprintf(fp, "X-Sylpheed-Sign:%d\n", compose->use_signing);
4172                 if (compose->use_encryption) {
4173                         gchar *encdata;
4174
4175                         encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
4176                         if (encdata != NULL) {
4177                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
4178                                         fprintf(fp, "X-Sylpheed-Encrypt:%d\n", compose->use_encryption);
4179                                         fprintf(fp, "X-Sylpheed-Encrypt-Data:%s\n", 
4180                                                 encdata);
4181                                 } /* else we finally dont want to encrypt */
4182                         } else {
4183                                 fprintf(fp, "X-Sylpheed-Encrypt:%d\n", compose->use_encryption);
4184                                 /* and if encdata was null, it means there's been a problem in 
4185                                  * key selection */
4186                         }
4187                         g_free(encdata);
4188                 }
4189         }
4190
4191         /* Save copy folder */
4192         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
4193                 gchar *savefolderid;
4194                 
4195                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
4196                 fprintf(fp, "SCF:%s\n", savefolderid);
4197                 g_free(savefolderid);
4198         }
4199         /* Message-ID of message replying to */
4200         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
4201                 gchar *folderid;
4202                 
4203                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
4204                 fprintf(fp, "RMID:%s\x7f%d\x7f%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid);
4205                 g_free(folderid);
4206         }
4207         /* Message-ID of message forwarding to */
4208         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
4209                 gchar *folderid;
4210                 
4211                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
4212                 fprintf(fp, "FMID:%s\x7f%d\x7f%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid);
4213                 g_free(folderid);
4214         }
4215         fprintf(fp, "\n");
4216
4217         if (compose->redirect_filename != NULL) {
4218                 if (compose_redirect_write_to_file(compose, fp) < 0) {
4219                         lock = FALSE;
4220                         fclose(fp);
4221                         g_unlink(tmp);
4222                         g_free(tmp);
4223                         return -2;
4224                 }
4225         } else {
4226                 gint result = 0;
4227                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND)) < 0) {
4228                         lock = FALSE;
4229                         fclose(fp);
4230                         g_unlink(tmp);
4231                         g_free(tmp);
4232                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
4233                 }
4234         }
4235
4236         if (fclose(fp) == EOF) {
4237                 FILE_OP_ERROR(tmp, "fclose");
4238                 g_unlink(tmp);
4239                 g_free(tmp);
4240                 return -2;
4241         }
4242
4243         queue = account_get_special_folder(compose->account, F_QUEUE);
4244         if (!queue) {
4245                 g_warning("can't find queue folder\n");
4246                 g_unlink(tmp);
4247                 g_free(tmp);
4248                 return -1;
4249         }
4250         folder_item_scan(queue);
4251         if ((num = folder_item_add_msg(queue, tmp, NULL, TRUE)) < 0) {
4252                 g_warning("can't queue the message\n");
4253                 g_unlink(tmp);
4254                 g_free(tmp);
4255                 return -1;
4256         }
4257         g_unlink(tmp);
4258         g_free(tmp);
4259
4260         if (compose->mode == COMPOSE_REEDIT) {
4261                 compose_remove_reedit_target(compose);
4262         }
4263
4264         if ((msgnum != NULL) && (item != NULL)) {
4265                 *msgnum = num;
4266                 *item = queue;
4267         }
4268
4269         return 0;
4270 }
4271
4272 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
4273 {
4274         AttachInfo *ainfo;
4275         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
4276         MimeInfo *mimepart;
4277         struct stat statbuf;
4278         gchar *type, *subtype;
4279         GtkTreeModel *model;
4280         GtkTreeIter iter;
4281
4282         model = gtk_tree_view_get_model(tree_view);
4283         
4284         if (!gtk_tree_model_get_iter_first(model, &iter))
4285                 return;
4286         do {
4287                 gtk_tree_model_get(model, &iter,
4288                                    COL_DATA, &ainfo,
4289                                    -1);
4290                                                            
4291                 mimepart = procmime_mimeinfo_new();
4292                 mimepart->content = MIMECONTENT_FILE;
4293                 mimepart->data.filename = g_strdup(ainfo->file);
4294                 mimepart->offset = 0;
4295
4296                 stat(ainfo->file, &statbuf);
4297                 mimepart->length = statbuf.st_size;
4298
4299                 type = g_strdup(ainfo->content_type);
4300
4301                 if (!strchr(type, '/')) {
4302                         g_free(type);
4303                         type = g_strdup("application/octet-stream");
4304                 }
4305
4306                 subtype = strchr(type, '/') + 1;
4307                 *(subtype - 1) = '\0';
4308                 mimepart->type = procmime_get_media_type(type);
4309                 mimepart->subtype = g_strdup(subtype);
4310                 g_free(type);
4311
4312                 if (mimepart->type == MIMETYPE_MESSAGE && 
4313                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
4314                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
4315                 } else {
4316                         g_hash_table_insert(mimepart->typeparameters,
4317                                             g_strdup("name"), g_strdup(ainfo->name));
4318                         g_hash_table_insert(mimepart->dispositionparameters,
4319                                             g_strdup("filename"), g_strdup(ainfo->name));
4320                         mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
4321                 }
4322
4323                 if (compose->use_signing) {
4324                         if (ainfo->encoding == ENC_7BIT)
4325                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
4326                         else if (ainfo->encoding == ENC_8BIT)
4327                                 ainfo->encoding = ENC_BASE64;
4328                 }
4329                 
4330                 procmime_encode_content(mimepart, ainfo->encoding);
4331
4332                 g_node_append(parent->node, mimepart->node);
4333         } while (gtk_tree_model_iter_next(model, &iter));
4334 }
4335
4336 #define QUOTE_IF_REQUIRED(out, str)                                     \
4337 {                                                                       \
4338         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4339                 gchar *__tmp;                                           \
4340                 gint len;                                               \
4341                                                                         \
4342                 len = strlen(str) + 3;                                  \
4343                 if ((__tmp = alloca(len)) == NULL) {                    \
4344                         g_warning("can't allocate memory\n");           \
4345                         g_string_free(header, TRUE);                    \
4346                         return NULL;                                    \
4347                 }                                                       \
4348                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4349                 out = __tmp;                                            \
4350         } else {                                                        \
4351                 gchar *__tmp;                                           \
4352                                                                         \
4353                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4354                         g_warning("can't allocate memory\n");           \
4355                         g_string_free(header, TRUE);                    \
4356                         return NULL;                                    \
4357                 } else                                                  \
4358                         strcpy(__tmp, str);                             \
4359                                                                         \
4360                 out = __tmp;                                            \
4361         }                                                               \
4362 }
4363
4364 #define IS_IN_CUSTOM_HEADER(header) \
4365         (compose->account->add_customhdr && \
4366          custom_header_find(compose->account->customhdr_list, header) != NULL)
4367
4368 static void compose_add_headerfield_from_headerlist(Compose *compose, 
4369                                                     GString *header, 
4370                                                     const gchar *fieldname,
4371                                                     const gchar *seperator)
4372 {
4373         gchar *str, *fieldname_w_colon, *trans_fieldname;
4374         gboolean add_field = FALSE;
4375         GSList *list;
4376         ComposeHeaderEntry *headerentry;
4377         const gchar * headerentryname;
4378         GString *fieldstr;
4379
4380         if (IS_IN_CUSTOM_HEADER(fieldname))
4381                 return;
4382
4383         debug_print("Adding %s-fields\n", fieldname);
4384
4385         fieldstr = g_string_sized_new(64);
4386
4387         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
4388         trans_fieldname = (prefs_common.trans_hdr ? gettext(fieldname_w_colon) : fieldname_w_colon);
4389
4390         for (list = compose->header_list; list; list = list->next) {
4391                 headerentry = ((ComposeHeaderEntry *)list->data);
4392                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
4393
4394                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
4395                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
4396                         g_strstrip(str);
4397                         if (str[0] != '\0') {
4398                                 if (add_field)
4399                                         g_string_append(fieldstr, seperator);
4400                                 g_string_append(fieldstr, str);
4401                                 add_field = TRUE;
4402                         }
4403                         g_free(str);
4404                 }
4405         }
4406         if (add_field) {
4407                 gchar *buf;
4408
4409                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
4410                 compose_convert_header
4411                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
4412                         strlen(fieldname) + 2, TRUE);
4413                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
4414                 g_free(buf);
4415         }
4416
4417         g_free(fieldname_w_colon);
4418         g_string_free(fieldstr, TRUE);
4419
4420         return;
4421 }
4422
4423 static gchar *compose_get_header(Compose *compose)
4424 {
4425         gchar buf[BUFFSIZE];
4426         const gchar *entry_str;
4427         gchar *str;
4428         gchar *name;
4429         GSList *list;
4430         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
4431         GString *header;
4432
4433         g_return_val_if_fail(compose->account != NULL, NULL);
4434         g_return_val_if_fail(compose->account->address != NULL, NULL);
4435
4436         header = g_string_sized_new(64);
4437
4438         /* Date */
4439         if (compose->account->add_date) {
4440                 get_rfc822_date(buf, sizeof(buf));
4441                 g_string_append_printf(header, "Date: %s\n", buf);
4442         }
4443
4444         /* From */
4445         if (compose->account->name && *compose->account->name) {
4446                 compose_convert_header
4447                         (compose, buf, sizeof(buf), compose->account->name,
4448                          strlen("From: "), TRUE);
4449                 QUOTE_IF_REQUIRED(name, buf);
4450                 g_string_append_printf(header, "From: %s <%s>\n",
4451                         name, compose->account->address);
4452         } else
4453                 g_string_append_printf(header, "From: %s\n", compose->account->address);
4454         
4455         /* To */
4456         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
4457
4458         /* Newsgroups */
4459         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
4460
4461         /* Cc */
4462         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
4463
4464         /* Bcc */
4465         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
4466
4467         /* Subject */
4468         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
4469
4470         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
4471                 g_strstrip(str);
4472                 if (*str != '\0') {
4473                         compose_convert_header(compose, buf, sizeof(buf), str,
4474                                                strlen("Subject: "), FALSE);
4475                         g_string_append_printf(header, "Subject: %s\n", buf);
4476                 }
4477         }
4478         g_free(str);
4479
4480         /* Message-ID */
4481         if (compose->account->gen_msgid) {
4482                 generate_msgid(buf, sizeof(buf));
4483                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
4484                 compose->msgid = g_strdup(buf);
4485         }
4486
4487         if (compose->remove_references == FALSE) {
4488                 /* In-Reply-To */
4489                 if (compose->inreplyto && compose->to_list)
4490                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
4491         
4492                 /* References */
4493                 if (compose->references)
4494                         g_string_append_printf(header, "References: %s\n", compose->references);
4495         }
4496
4497         /* Followup-To */
4498         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
4499
4500         /* Reply-To */
4501         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
4502
4503         /* Organization */
4504         if (compose->account->organization &&
4505             strlen(compose->account->organization) &&
4506             !IS_IN_CUSTOM_HEADER("Organization")) {
4507                 compose_convert_header(compose, buf, sizeof(buf),
4508                                        compose->account->organization,
4509                                        strlen("Organization: "), FALSE);
4510                 g_string_append_printf(header, "Organization: %s\n", buf);
4511         }
4512
4513         /* Program version and system info */
4514         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
4515             !compose->newsgroup_list) {
4516                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
4517                         prog_version,
4518                         gtk_major_version, gtk_minor_version, gtk_micro_version,
4519                         TARGET_ALIAS);
4520         }
4521         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
4522                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
4523                         prog_version,
4524                         gtk_major_version, gtk_minor_version, gtk_micro_version,
4525                         TARGET_ALIAS);
4526         }
4527
4528         /* custom headers */
4529         if (compose->account->add_customhdr) {
4530                 GSList *cur;
4531
4532                 for (cur = compose->account->customhdr_list; cur != NULL;
4533                      cur = cur->next) {
4534                         CustomHeader *chdr = (CustomHeader *)cur->data;
4535
4536                         if (custom_header_is_allowed(chdr->name)) {
4537                                 compose_convert_header
4538                                         (compose, buf, sizeof(buf),
4539                                          chdr->value ? chdr->value : "",
4540                                          strlen(chdr->name) + 2, FALSE);
4541                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
4542                         }
4543                 }
4544         }
4545
4546         /* PRIORITY */
4547         switch (compose->priority) {
4548                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
4549                                                    "X-Priority: 1 (Highest)\n");
4550                         break;
4551                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
4552                                                 "X-Priority: 2 (High)\n");
4553                         break;
4554                 case PRIORITY_NORMAL: break;
4555                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
4556                                                "X-Priority: 4 (Low)\n");
4557                         break;
4558                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
4559                                                   "X-Priority: 5 (Lowest)\n");
4560                         break;
4561                 default: debug_print("compose: priority unknown : %d\n",
4562                                      compose->priority);
4563         }
4564
4565         /* Request Return Receipt */
4566         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
4567                 if (compose->return_receipt) {
4568                         if (compose->account->name
4569                             && *compose->account->name) {
4570                                 compose_convert_header(compose, buf, sizeof(buf), 
4571                                                        compose->account->name, 
4572                                                        strlen("Disposition-Notification-To: "),
4573                                                        TRUE);
4574                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
4575                         } else
4576                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
4577                 }
4578         }
4579
4580         /* get special headers */
4581         for (list = compose->header_list; list; list = list->next) {
4582                 ComposeHeaderEntry *headerentry;
4583                 gchar *tmp;
4584                 gchar *headername;
4585                 gchar *headername_wcolon;
4586                 gchar *headername_trans;
4587                 gchar *headervalue;
4588                 gchar **string;
4589                 gboolean standard_header = FALSE;
4590
4591                 headerentry = ((ComposeHeaderEntry *)list->data);
4592                 
4593                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry)));
4594                 if (strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
4595                         g_free(tmp);
4596                         continue;
4597                 }
4598
4599                 if (!strstr(tmp, ":")) {
4600                         headername_wcolon = g_strconcat(tmp, ":", NULL);
4601                         headername = g_strdup(tmp);
4602                 } else {
4603                         headername_wcolon = g_strdup(tmp);
4604                         headername = g_strdup(strtok(tmp, ":"));
4605                 }
4606                 g_free(tmp);
4607                 
4608                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4609                 Xstrdup_a(headervalue, entry_str, return NULL);
4610                 subst_char(headervalue, '\r', ' ');
4611                 subst_char(headervalue, '\n', ' ');
4612                 string = std_headers;
4613                 while (*string != NULL) {
4614                         headername_trans = prefs_common.trans_hdr ? gettext(*string) : *string;
4615                         if (!strcmp(headername_trans,headername_wcolon))
4616                                 standard_header = TRUE;
4617                         string++;
4618                 }
4619                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
4620                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
4621                                 
4622                 g_free(headername);
4623                 g_free(headername_wcolon);              
4624         }
4625
4626         str = header->str;
4627         g_string_free(header, FALSE);
4628
4629         return str;
4630 }
4631
4632 #undef IS_IN_CUSTOM_HEADER
4633
4634 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
4635                                    gint header_len, gboolean addr_field)
4636 {
4637         gchar *tmpstr = NULL;
4638         const gchar *out_codeset = NULL;
4639
4640         g_return_if_fail(src != NULL);
4641         g_return_if_fail(dest != NULL);
4642
4643         if (len < 1) return;
4644
4645         tmpstr = g_strdup(src);
4646
4647         subst_char(tmpstr, '\n', ' ');
4648         subst_char(tmpstr, '\r', ' ');
4649         g_strchomp(tmpstr);
4650
4651         if (!g_utf8_validate(tmpstr, -1, NULL)) {
4652                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
4653                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
4654                 g_free(tmpstr);
4655                 tmpstr = mybuf;
4656         }
4657
4658         codeconv_set_strict(TRUE);
4659         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
4660                 conv_get_charset_str(compose->out_encoding));
4661         codeconv_set_strict(FALSE);
4662         
4663         if (!dest || *dest == '\0') {
4664                 gchar *test_conv_global_out = NULL;
4665                 gchar *test_conv_reply = NULL;
4666
4667                 /* automatic mode. be automatic. */
4668                 codeconv_set_strict(TRUE);
4669
4670                 out_codeset = conv_get_outgoing_charset_str();
4671                 if (out_codeset) {
4672                         debug_print("trying to convert to %s\n", out_codeset);
4673                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
4674                 }
4675
4676                 if (!test_conv_global_out && compose->orig_charset) {
4677                         out_codeset = compose->orig_charset;
4678                         debug_print("failure; trying to convert to %s\n", out_codeset);
4679                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
4680                 }
4681
4682                 if (!test_conv_global_out && !test_conv_reply) {
4683                         /* we're lost */
4684                         out_codeset = CS_INTERNAL;
4685                         debug_print("finally using %s\n", out_codeset);
4686                 }
4687                 g_free(test_conv_global_out);
4688                 g_free(test_conv_reply);
4689                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
4690                                         out_codeset);
4691                 codeconv_set_strict(FALSE);
4692         }
4693         g_free(tmpstr);
4694 }
4695
4696 static void compose_create_header_entry(Compose *compose) 
4697 {
4698         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
4699
4700         GtkWidget *combo;
4701         GtkWidget *entry;
4702         GList *combo_list = NULL;
4703         gchar **string;
4704         const gchar *header = NULL;
4705         ComposeHeaderEntry *headerentry;
4706         gboolean standard_header = FALSE;
4707
4708         headerentry = g_new0(ComposeHeaderEntry, 1);
4709
4710         /* Combo box */
4711         combo = gtk_combo_new();
4712         string = headers; 
4713         while(*string != NULL) {
4714                 combo_list = g_list_append(combo_list, (prefs_common.trans_hdr ? gettext(*string) : *string));
4715                 string++;
4716         }
4717         gtk_combo_set_popdown_strings(GTK_COMBO(combo), combo_list);
4718         g_list_free(combo_list);
4719         gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(combo)->entry), TRUE);
4720         g_signal_connect(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
4721                          G_CALLBACK(compose_grab_focus_cb), compose);
4722         gtk_widget_show(combo);
4723         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1, compose->header_nextrow, compose->header_nextrow+1, GTK_SHRINK, GTK_FILL, 0, 0);
4724         if (compose->header_last) {     
4725                 const gchar *last_header_entry = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
4726                 string = headers;
4727                 while (*string != NULL) {
4728                         if (!strcmp(*string, last_header_entry))
4729                                 standard_header = TRUE;
4730                         string++;
4731                 }
4732                 if (standard_header)
4733                         header = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
4734         }
4735         if (!compose->header_last || !standard_header) {
4736                 switch(compose->account->protocol) {
4737                         case A_NNTP:
4738                                 header = prefs_common.trans_hdr ? _("Newsgroups:") : "Newsgroups:";
4739                                 break;
4740                         default:
4741                                 header = prefs_common.trans_hdr ? _("To:") : "To:";
4742                                 break;
4743                 }                                                                   
4744         }
4745         if (header)
4746                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), header);
4747
4748         g_signal_connect_after(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
4749                          G_CALLBACK(compose_grab_focus_cb), compose);
4750
4751         /* Entry field */
4752         entry = gtk_entry_new(); 
4753         gtk_widget_show(entry);
4754         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2, compose->header_nextrow, compose->header_nextrow+1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
4755
4756         g_signal_connect(G_OBJECT(entry), "key-press-event", 
4757                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
4758                          headerentry);
4759         g_signal_connect(G_OBJECT(entry), "changed", 
4760                          G_CALLBACK(compose_headerentry_changed_cb), 
4761                          headerentry);
4762         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
4763                          G_CALLBACK(compose_grab_focus_cb), compose);
4764                          
4765         /* email dnd */
4766         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
4767                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
4768                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
4769         g_signal_connect(G_OBJECT(entry), "drag_data_received",
4770                          G_CALLBACK(compose_header_drag_received_cb),
4771                          entry);
4772         g_signal_connect(G_OBJECT(entry), "drag-drop",
4773                          G_CALLBACK(compose_drag_drop),
4774                          compose);
4775         
4776         address_completion_register_entry(GTK_ENTRY(entry));
4777
4778         headerentry->compose = compose;
4779         headerentry->combo = combo;
4780         headerentry->entry = entry;
4781         headerentry->headernum = compose->header_nextrow;
4782
4783         compose->header_nextrow++;
4784         compose->header_last = headerentry;
4785 }
4786
4787 static void compose_add_header_entry(Compose *compose, gchar *header, gchar *text) 
4788 {
4789         ComposeHeaderEntry *last_header;
4790         
4791         last_header = compose->header_last;
4792         
4793         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(last_header->combo)->entry), header);
4794         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
4795 }
4796
4797 static GtkWidget *compose_create_header(Compose *compose) 
4798 {
4799         GtkWidget *label;
4800         GtkWidget *hbox;
4801         GtkWidget *from_optmenu_hbox;
4802         GtkWidget *header_scrolledwin;
4803         GtkWidget *header_table;
4804
4805         gint count = 0;
4806
4807         /* header labels and entries */
4808         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
4809         gtk_widget_show(header_scrolledwin);
4810         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
4811
4812         header_table = gtk_table_new(2, 2, FALSE);
4813         gtk_widget_show(header_table);
4814         gtk_container_set_border_width(GTK_CONTAINER(header_table), 2);
4815         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
4816         gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_ETCHED_IN);
4817         count = 0;
4818
4819         /* option menu for selecting accounts */
4820         hbox = gtk_hbox_new(FALSE, 0);
4821         label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
4822         gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
4823         gtk_table_attach(GTK_TABLE(header_table), hbox, 0, 1, count, count + 1,
4824                          GTK_FILL, 0, 2, 0);
4825         from_optmenu_hbox = compose_account_option_menu_create(compose);
4826         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
4827                                   1, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
4828         count++;
4829
4830         compose->header_table = header_table;
4831         compose->header_list = NULL;
4832         compose->header_nextrow = count;
4833
4834         compose_create_header_entry(compose);
4835
4836         compose->table            = NULL;
4837
4838         return header_scrolledwin ;
4839 }
4840
4841 GtkWidget *compose_create_attach(Compose *compose)
4842 {
4843         GtkWidget *attach_scrwin;
4844         GtkWidget *attach_clist;
4845
4846         GtkListStore *store;
4847         GtkCellRenderer *renderer;
4848         GtkTreeViewColumn *column;
4849         GtkTreeSelection *selection;
4850
4851         /* attachment list */
4852         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
4853         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
4854                                        GTK_POLICY_AUTOMATIC,
4855                                        GTK_POLICY_AUTOMATIC);
4856         gtk_widget_set_size_request(attach_scrwin, -1, 80);
4857
4858         store = gtk_list_store_new(N_ATTACH_COLS, 
4859                                    G_TYPE_STRING,
4860                                    G_TYPE_STRING,
4861                                    G_TYPE_STRING,
4862                                    G_TYPE_POINTER,
4863                                    G_TYPE_AUTO_POINTER,
4864                                    -1);
4865         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
4866                                         (GTK_TREE_MODEL(store)));
4867         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
4868         g_object_unref(store);
4869         
4870         renderer = gtk_cell_renderer_text_new();
4871         column = gtk_tree_view_column_new_with_attributes
4872                         (_("Mime type"), renderer, "text", 
4873                          COL_MIMETYPE, NULL);
4874         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
4875         
4876         renderer = gtk_cell_renderer_text_new();
4877         column = gtk_tree_view_column_new_with_attributes
4878                         (_("Size"), renderer, "text", 
4879                          COL_SIZE, NULL);
4880         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
4881         
4882         renderer = gtk_cell_renderer_text_new();
4883         column = gtk_tree_view_column_new_with_attributes
4884                         (_("Name"), renderer, "text", 
4885                          COL_NAME, NULL);
4886         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
4887
4888         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
4889                                      prefs_common.enable_rules_hint);
4890         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
4891         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
4892
4893         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
4894                          G_CALLBACK(attach_selected), compose);
4895         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
4896                          G_CALLBACK(attach_button_pressed), compose);
4897         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
4898                          G_CALLBACK(attach_key_pressed), compose);
4899
4900         /* drag and drop */
4901         gtk_drag_dest_set(attach_clist,
4902                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
4903                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
4904                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
4905         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
4906                          G_CALLBACK(compose_attach_drag_received_cb),
4907                          compose);
4908         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
4909                          G_CALLBACK(compose_drag_drop),
4910                          compose);
4911
4912         compose->attach_scrwin = attach_scrwin;
4913         compose->attach_clist  = attach_clist;
4914
4915         return attach_scrwin;
4916 }
4917
4918 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
4919 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
4920
4921 static GtkWidget *compose_create_others(Compose *compose)
4922 {
4923         GtkWidget *table;
4924         GtkWidget *savemsg_checkbtn;
4925         GtkWidget *savemsg_entry;
4926         GtkWidget *savemsg_select;
4927         
4928         guint rowcount = 0;
4929         gchar *folderidentifier;
4930
4931         /* Table for settings */
4932         table = gtk_table_new(3, 1, FALSE);
4933         gtk_widget_show(table);
4934         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
4935         rowcount = 0;
4936
4937         /* Save Message to folder */
4938         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
4939         gtk_widget_show(savemsg_checkbtn);
4940         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
4941         if (account_get_special_folder(compose->account, F_OUTBOX)) {
4942                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
4943         }
4944         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
4945                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
4946
4947         savemsg_entry = gtk_entry_new();
4948         gtk_widget_show(savemsg_entry);
4949         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
4950         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
4951         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
4952                          G_CALLBACK(compose_grab_focus_cb), compose);
4953         if (account_get_special_folder(compose->account, F_OUTBOX)) {
4954                 folderidentifier = folder_item_get_identifier(account_get_special_folder
4955                                   (compose->account, F_OUTBOX));
4956                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
4957                 g_free(folderidentifier);
4958         }
4959
4960         savemsg_select = gtk_button_new_with_label (_("Select ..."));
4961         gtk_widget_show(savemsg_select);
4962         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
4963         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
4964                          G_CALLBACK(compose_savemsg_select_cb),
4965                          compose);
4966
4967         rowcount++;
4968
4969         compose->savemsg_checkbtn = savemsg_checkbtn;
4970         compose->savemsg_entry = savemsg_entry;
4971
4972         return table;   
4973 }
4974
4975 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
4976 {
4977         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
4978                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
4979 }
4980
4981 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
4982 {
4983         FolderItem *dest;
4984         gchar * path;
4985
4986         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
4987         if (!dest) return;
4988
4989         path = folder_item_get_identifier(dest);
4990
4991         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
4992         g_free(path);
4993 }
4994
4995 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
4996                                   GdkAtom clip);
4997
4998 #define BLOCK_WRAP() {                                                  \
4999         prev_autowrap = compose->autowrap;                              \
5000         buffer = gtk_text_view_get_buffer(                              \
5001                                         GTK_TEXT_VIEW(compose->text));  \
5002         compose->autowrap = FALSE;                                      \
5003                                                                         \
5004         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
5005                                 G_CALLBACK(compose_changed_cb),         \
5006                                 compose);                               \
5007         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
5008                                 G_CALLBACK(text_inserted),              \
5009                                 compose);                               \
5010 }
5011 #define UNBLOCK_WRAP() {                                                \
5012         compose->autowrap = prev_autowrap;                              \
5013         if (compose->autowrap)                                          \
5014                 compose_wrap_all(compose);                              \
5015                                                                         \
5016         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
5017                                 G_CALLBACK(compose_changed_cb),         \
5018                                 compose);                               \
5019         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
5020                                 G_CALLBACK(text_inserted),              \
5021                                 compose);                               \
5022 }
5023
5024
5025 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
5026                                        Compose *compose)
5027 {
5028         gint prev_autowrap;
5029         GtkTextBuffer *buffer;
5030         if (event->button == 2) {
5031                 BLOCK_WRAP();
5032                 entry_paste_clipboard(compose, compose->focused_editable, 
5033                                 prefs_common.linewrap_pastes,
5034                                 GDK_SELECTION_PRIMARY);
5035                 UNBLOCK_WRAP();
5036                 return TRUE;
5037         }
5038         return FALSE;
5039 }
5040
5041 static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
5042 {
5043         Compose   *compose;
5044         GtkWidget *window;
5045         GtkWidget *vbox;
5046         GtkWidget *menubar;
5047         GtkWidget *handlebox;
5048
5049         GtkWidget *notebook;
5050
5051         GtkWidget *vbox2;
5052
5053         GtkWidget *label;
5054         GtkWidget *subject_hbox;
5055         GtkWidget *subject_frame;
5056         GtkWidget *subject_entry;
5057         GtkWidget *subject;
5058         GtkWidget *paned;
5059
5060         GtkWidget *edit_vbox;
5061         GtkWidget *ruler_hbox;
5062         GtkWidget *ruler;
5063         GtkWidget *scrolledwin;
5064         GtkWidget *text;
5065         GtkTextBuffer *buffer;
5066         GtkClipboard *clipboard;
5067
5068         UndoMain *undostruct;
5069
5070         gchar *titles[N_ATTACH_COLS];
5071         guint n_menu_entries;
5072         GdkColormap *cmap;
5073         GdkColor color[1];
5074         gboolean success[1];
5075         GtkWidget *popupmenu;
5076         GtkItemFactory *popupfactory;
5077         GtkItemFactory *ifactory;
5078         GtkWidget *tmpl_menu;
5079         gint n_entries;
5080         GtkWidget *menuitem;
5081
5082 #if USE_ASPELL
5083         GtkAspell * gtkaspell = NULL;
5084 #endif
5085
5086         static GdkGeometry geometry;
5087
5088         g_return_val_if_fail(account != NULL, NULL);
5089
5090         debug_print("Creating compose window...\n");
5091         compose = g_new0(Compose, 1);
5092
5093         titles[COL_MIMETYPE] = _("MIME type");
5094         titles[COL_SIZE]     = _("Size");
5095         titles[COL_NAME]     = _("Name");
5096
5097         compose->account = account;
5098         
5099         compose->mutex = g_mutex_new();
5100         
5101         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
5102         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
5103         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
5104
5105         if (!geometry.max_width) {
5106                 geometry.max_width = gdk_screen_width();
5107                 geometry.max_height = gdk_screen_height();
5108         }
5109
5110         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
5111                                       &geometry, GDK_HINT_MAX_SIZE);
5112         if (!geometry.min_width) {
5113                 geometry.min_width = 600;
5114                 geometry.min_height = 480;
5115         }
5116         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
5117                                       &geometry, GDK_HINT_MIN_SIZE);
5118         gtk_widget_set_uposition(window, prefs_common.compose_x, 
5119                                  prefs_common.compose_y);
5120
5121         g_signal_connect(G_OBJECT(window), "delete_event",
5122                          G_CALLBACK(compose_delete_cb), compose);
5123         MANAGE_WINDOW_SIGNALS_CONNECT(window);
5124         gtk_widget_realize(window);
5125
5126         gtkut_widget_set_composer_icon(window);
5127
5128         vbox = gtk_vbox_new(FALSE, 0);
5129         gtk_container_add(GTK_CONTAINER(window), vbox);
5130
5131         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
5132         menubar = menubar_create(window, compose_entries,
5133                                  n_menu_entries, "<Compose>", compose);
5134         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
5135
5136         if (prefs_common.toolbar_detachable) {
5137                 handlebox = gtk_handle_box_new();
5138         } else {
5139                 handlebox = gtk_hbox_new(FALSE, 0);
5140         }
5141         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
5142
5143         gtk_widget_realize(handlebox);
5144         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
5145                                           (gpointer)compose);
5146
5147         vbox2 = gtk_vbox_new(FALSE, 2);
5148         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
5149         gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
5150         
5151         /* Notebook */
5152         notebook = gtk_notebook_new();
5153         gtk_widget_set_size_request(notebook, -1, 130);
5154         gtk_widget_show(notebook);
5155
5156         /* header labels and entries */
5157         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), compose_create_header(compose), gtk_label_new(_("Header")));
5158         /* attachment list */
5159         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), compose_create_attach(compose), gtk_label_new(_("Attachments")));
5160         /* Others Tab */
5161         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), compose_create_others(compose), gtk_label_new(_("Others")));
5162
5163         /* Subject */
5164         subject_hbox = gtk_hbox_new(FALSE, 0);
5165         gtk_widget_show(subject_hbox);
5166
5167         subject_frame = gtk_frame_new(NULL);
5168         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_OUT);
5169         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, BORDER_WIDTH+1);
5170         gtk_widget_show(subject_frame);
5171
5172         subject = gtk_hbox_new(FALSE, 0);
5173         gtk_container_set_border_width(GTK_CONTAINER(subject), BORDER_WIDTH);
5174         gtk_widget_show(subject);
5175
5176         label = gtk_label_new(_("Subject:"));
5177         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 4);
5178         gtk_widget_show(label);
5179
5180         subject_entry = gtk_entry_new();
5181         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 2);
5182         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
5183                          G_CALLBACK(compose_grab_focus_cb), compose);
5184         gtk_widget_show(subject_entry);
5185         compose->subject_entry = subject_entry;
5186         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
5187         
5188         edit_vbox = gtk_vbox_new(FALSE, 0);
5189
5190         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
5191
5192         /* ruler */
5193         ruler_hbox = gtk_hbox_new(FALSE, 0);
5194         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
5195
5196         ruler = gtk_shruler_new();
5197         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
5198         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
5199                            BORDER_WIDTH);
5200
5201         /* text widget */
5202         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
5203         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
5204                                        GTK_POLICY_AUTOMATIC,
5205                                        GTK_POLICY_AUTOMATIC);
5206         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
5207                                             GTK_SHADOW_IN);
5208         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
5209         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
5210
5211         text = gtk_text_view_new();
5212         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
5213         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
5214         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
5215         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
5216         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
5217         
5218         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
5219
5220         g_signal_connect_after(G_OBJECT(text), "size_allocate",
5221                                G_CALLBACK(compose_edit_size_alloc),
5222                                ruler);
5223         g_signal_connect(G_OBJECT(buffer), "changed",
5224                          G_CALLBACK(compose_changed_cb), compose);
5225         g_signal_connect(G_OBJECT(text), "grab_focus",
5226                          G_CALLBACK(compose_grab_focus_cb), compose);
5227         g_signal_connect(G_OBJECT(buffer), "insert_text",
5228                          G_CALLBACK(text_inserted), compose);
5229         g_signal_connect(G_OBJECT(text), "button_press_event",
5230                          G_CALLBACK(text_clicked), compose);
5231
5232         /* drag and drop */
5233         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
5234                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
5235                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
5236         g_signal_connect(G_OBJECT(text), "drag_data_received",
5237                          G_CALLBACK(compose_insert_drag_received_cb),
5238                          compose);
5239         g_signal_connect(G_OBJECT(text), "drag-drop",
5240                          G_CALLBACK(compose_drag_drop),
5241                          compose);
5242         gtk_widget_show_all(vbox);
5243
5244         /* pane between attach clist and text */
5245         paned = gtk_vpaned_new();
5246         gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
5247         gtk_container_add(GTK_CONTAINER(vbox2), paned);
5248         gtk_paned_add1(GTK_PANED(paned), notebook);
5249         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
5250         gtk_widget_show_all(paned);
5251
5252
5253         if (prefs_common.textfont) {
5254                 PangoFontDescription *font_desc;
5255
5256                 font_desc = pango_font_description_from_string
5257                         (prefs_common.textfont);
5258                 if (font_desc) {
5259                         gtk_widget_modify_font(text, font_desc);
5260                         pango_font_description_free(font_desc);
5261                 }
5262         }
5263
5264         color[0] = quote_color;
5265         cmap = gdk_drawable_get_colormap(window->window);
5266         gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success);
5267         if (success[0] == FALSE) {
5268                 GtkStyle *style;
5269
5270                 g_warning("Compose: color allocation failed.\n");
5271                 style = gtk_widget_get_style(text);
5272                 quote_color = style->black;
5273         }
5274
5275         n_entries = sizeof(compose_popup_entries) /
5276                 sizeof(compose_popup_entries[0]);
5277         popupmenu = menu_create_items(compose_popup_entries, n_entries,
5278                                       "<Compose>", &popupfactory,
5279                                       compose);
5280
5281         ifactory = gtk_item_factory_from_widget(menubar);
5282         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
5283         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
5284         menu_set_sensitive(ifactory, "/Options/Remove references", FALSE);
5285
5286         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
5287
5288         undostruct = undo_init(text);
5289         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
5290                                    menubar);
5291
5292         address_completion_start(window);
5293
5294         compose->window        = window;
5295         compose->vbox          = vbox;
5296         compose->menubar       = menubar;
5297         compose->handlebox     = handlebox;
5298
5299         compose->vbox2         = vbox2;
5300
5301         compose->paned = paned;
5302
5303         compose->edit_vbox     = edit_vbox;
5304         compose->ruler_hbox    = ruler_hbox;
5305         compose->ruler         = ruler;
5306         compose->scrolledwin   = scrolledwin;
5307         compose->text          = text;
5308
5309         compose->focused_editable = NULL;
5310
5311         compose->popupmenu    = popupmenu;
5312         compose->popupfactory = popupfactory;
5313
5314         compose->tmpl_menu = tmpl_menu;
5315
5316         compose->mode = mode;
5317
5318         compose->targetinfo = NULL;
5319         compose->replyinfo  = NULL;
5320         compose->fwdinfo    = NULL;
5321
5322         compose->replyto     = NULL;
5323         compose->cc          = NULL;
5324         compose->bcc         = NULL;
5325         compose->followup_to = NULL;
5326
5327         compose->ml_post     = NULL;
5328
5329         compose->inreplyto   = NULL;
5330         compose->references  = NULL;
5331         compose->msgid       = NULL;
5332         compose->boundary    = NULL;
5333
5334         compose->autowrap       = prefs_common.autowrap;
5335
5336         compose->use_signing    = FALSE;
5337         compose->use_encryption = FALSE;
5338         compose->privacy_system = NULL;
5339
5340         compose->modified = FALSE;
5341
5342         compose->return_receipt = FALSE;
5343
5344         compose->to_list        = NULL;
5345         compose->newsgroup_list = NULL;
5346
5347         compose->undostruct = undostruct;
5348
5349         compose->sig_str = NULL;
5350
5351         compose->exteditor_file    = NULL;
5352         compose->exteditor_pid     = -1;
5353         compose->exteditor_tag     = -1;
5354         compose->draft_timeout_tag = -1;
5355
5356 #if USE_ASPELL
5357         menu_set_sensitive(ifactory, "/Spelling", FALSE);
5358         if (mode != COMPOSE_REDIRECT) {
5359                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
5360                     strcmp(prefs_common.dictionary, _("None"))) {
5361                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
5362                                                   prefs_common.dictionary,
5363                                                   conv_get_locale_charset_str(),
5364                                                   prefs_common.misspelled_col,
5365                                                   prefs_common.check_while_typing,
5366                                                   prefs_common.use_alternate,
5367                                                   GTK_TEXT_VIEW(text),
5368                                                   GTK_WINDOW(compose->window));
5369                         if (!gtkaspell) {
5370                                 alertpanel_error(_("Spell checker could not "
5371                                                 "be started.\n%s"),
5372                                                 gtkaspell_checkers_strerror());
5373                                 gtkaspell_checkers_reset_error();
5374                         } else {
5375
5376                                 GtkWidget *menuitem;
5377
5378                                 if (!gtkaspell_set_sug_mode(gtkaspell,
5379                                                 prefs_common.aspell_sugmode)) {
5380                                         debug_print("Aspell: could not set "
5381                                                     "suggestion mode %s\n",
5382                                                     gtkaspell_checkers_strerror());
5383                                         gtkaspell_checkers_reset_error();
5384                                 }
5385
5386                                 menuitem = gtk_item_factory_get_item(ifactory,
5387                                         "/Spelling/Spelling Configuration");
5388                                 gtkaspell_populate_submenu(gtkaspell, menuitem);
5389                                 menu_set_sensitive(ifactory, "/Spelling", TRUE);
5390                         }
5391                 }
5392         }
5393         compose->gtkaspell = gtkaspell;
5394 #endif
5395
5396         compose_select_account(compose, account, TRUE);
5397
5398         menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
5399         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
5400                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
5401
5402         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
5403                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
5404         
5405         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
5406                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
5407
5408
5409         if (account->protocol != A_NNTP)
5410                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry), prefs_common.trans_hdr ? _("To:") : "To:");
5411         else
5412                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry), prefs_common.trans_hdr ? _("Newsgroups:") : "Newsgroups:");
5413
5414         addressbook_set_target_compose(compose);
5415         
5416         if (mode != COMPOSE_REDIRECT)
5417                 compose_set_template_menu(compose);
5418         else {
5419                 menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Template");
5420                 menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
5421         }
5422
5423         compose_list = g_list_append(compose_list, compose);
5424
5425         if (!prefs_common.show_ruler)
5426                 gtk_widget_hide(ruler_hbox);
5427                 
5428         menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Show ruler");
5429         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
5430                                        prefs_common.show_ruler);
5431
5432         /* Priority */
5433         compose->priority = PRIORITY_NORMAL;
5434         compose_update_priority_menu_item(compose);
5435
5436         compose_set_out_encoding(compose);
5437         
5438         /* Actions menu */
5439         compose_update_actions_menu(compose);
5440
5441         /* Privacy Systems menu */
5442         compose_update_privacy_systems_menu(compose);
5443
5444         activate_privacy_system(compose, account, TRUE);
5445         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
5446         gtk_widget_show(window);
5447         
5448         return compose;
5449 }
5450
5451 static GtkWidget *compose_account_option_menu_create(Compose *compose)
5452 {
5453         GList *accounts;
5454         GtkWidget *hbox;
5455         GtkWidget *optmenu;
5456         GtkWidget *menu;
5457         gint num = 0, def_menu = 0;
5458
5459         accounts = account_get_list();
5460         g_return_val_if_fail(accounts != NULL, NULL);
5461
5462         hbox = gtk_hbox_new(FALSE, 0);
5463         optmenu = gtk_option_menu_new();
5464         gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
5465         menu = gtk_menu_new();
5466
5467         for (; accounts != NULL; accounts = accounts->next, num++) {
5468                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
5469                 GtkWidget *menuitem;
5470                 gchar *name;
5471
5472                 if (ac == compose->account) def_menu = num;
5473
5474                 if (ac->name)
5475                         name = g_strdup_printf("%s: %s <%s>",
5476                                                ac->account_name,
5477                                                ac->name, ac->address);
5478                 else
5479                         name = g_strdup_printf("%s: %s",
5480                                                ac->account_name, ac->address);
5481                 MENUITEM_ADD(menu, menuitem, name, ac->account_id);
5482                 g_free(name);
5483                 g_signal_connect(G_OBJECT(menuitem), "activate",
5484                                  G_CALLBACK(account_activated),
5485                                  compose);
5486         }
5487
5488         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
5489         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu);
5490
5491         return hbox;
5492 }
5493
5494 static void compose_set_priority_cb(gpointer data,
5495                                     guint action,
5496                                     GtkWidget *widget)
5497 {
5498         Compose *compose = (Compose *) data;
5499         compose->priority = action;
5500 }
5501
5502 static void compose_update_priority_menu_item(Compose * compose)
5503 {
5504         GtkItemFactory *ifactory;
5505         GtkWidget *menuitem = NULL;
5506
5507         ifactory = gtk_item_factory_from_widget(compose->menubar);
5508         
5509         switch (compose->priority) {
5510                 case PRIORITY_HIGHEST:
5511                         menuitem = gtk_item_factory_get_item
5512                                 (ifactory, "/Options/Priority/Highest");
5513                         break;
5514                 case PRIORITY_HIGH:
5515                         menuitem = gtk_item_factory_get_item
5516                                 (ifactory, "/Options/Priority/High");
5517                         break;
5518                 case PRIORITY_NORMAL:
5519                         menuitem = gtk_item_factory_get_item
5520                                 (ifactory, "/Options/Priority/Normal");
5521                         break;
5522                 case PRIORITY_LOW:
5523                         menuitem = gtk_item_factory_get_item
5524                                 (ifactory, "/Options/Priority/Low");
5525                         break;
5526                 case PRIORITY_LOWEST:
5527                         menuitem = gtk_item_factory_get_item
5528                                 (ifactory, "/Options/Priority/Lowest");
5529                         break;
5530         }
5531         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
5532 }       
5533
5534 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
5535 {
5536         Compose *compose = (Compose *) data;
5537         gchar *systemid;
5538         GtkItemFactory *ifactory;
5539         gboolean can_sign = FALSE, can_encrypt = FALSE;
5540
5541         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
5542
5543         if (!GTK_CHECK_MENU_ITEM(widget)->active)
5544                 return;
5545
5546         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
5547         g_free(compose->privacy_system);
5548         compose->privacy_system = NULL;
5549         if (systemid != NULL) {
5550                 compose->privacy_system = g_strdup(systemid);
5551
5552                 can_sign = privacy_system_can_sign(systemid);
5553                 can_encrypt = privacy_system_can_encrypt(systemid);
5554         }
5555
5556         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
5557
5558         ifactory = gtk_item_factory_from_widget(compose->menubar);
5559         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
5560         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
5561 }
5562
5563 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
5564 {
5565         static gchar *branch_path = "/Options/Privacy System";
5566         GtkItemFactory *ifactory;
5567         GtkWidget *menuitem = NULL;
5568         GList *amenu;
5569         gboolean can_sign = FALSE, can_encrypt = FALSE;
5570         gboolean found = FALSE;
5571
5572         ifactory = gtk_item_factory_from_widget(compose->menubar);
5573
5574         if (compose->privacy_system != NULL) {
5575                 gchar *systemid;
5576
5577                 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
5578                 g_return_if_fail(menuitem != NULL);
5579
5580                 amenu = GTK_MENU_SHELL(menuitem)->children;
5581                 menuitem = NULL;
5582                 while (amenu != NULL) {
5583                         GList *alist = amenu->next;
5584
5585                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
5586                         if (systemid != NULL)
5587                                 if (strcmp(systemid, compose->privacy_system) == 0) {
5588                                         menuitem = GTK_WIDGET(amenu->data);
5589
5590                                         can_sign = privacy_system_can_sign(systemid);
5591                                         can_encrypt = privacy_system_can_encrypt(systemid);
5592                                         found = TRUE;
5593                                         break;
5594                                 }
5595
5596                         amenu = alist;
5597                 }
5598                 if (menuitem != NULL)
5599                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
5600                 
5601                 if (warn && !found && strlen(compose->privacy_system)) {
5602                         gchar *tmp = g_strdup_printf(
5603                                 _("The privacy system '%s' cannot be loaded. You "
5604                                   "will not be able to sign or encrypt this message."),
5605                                   compose->privacy_system);
5606                         alertpanel_warning(tmp);
5607                         g_free(tmp);
5608                 }
5609         }
5610
5611         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
5612         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
5613 }       
5614  
5615 static void compose_set_out_encoding(Compose *compose)
5616 {
5617         GtkItemFactoryEntry *entry;
5618         GtkItemFactory *ifactory;
5619         CharSet out_encoding;
5620         gchar *path, *p, *q;
5621         GtkWidget *item;
5622
5623         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
5624         ifactory = gtk_item_factory_from_widget(compose->menubar);
5625
5626         for (entry = compose_entries; entry->callback != compose_address_cb;
5627              entry++) {
5628                 if (entry->callback == compose_set_encoding_cb &&
5629                     (CharSet)entry->callback_action == out_encoding) {
5630                         p = q = path = g_strdup(entry->path);
5631                         while (*p) {
5632                                 if (*p == '_') {
5633                                         if (p[1] == '_') {
5634                                                 p++;
5635                                                 *q++ = '_';
5636                                         }
5637                                 } else
5638                                         *q++ = *p;
5639                                 p++;
5640                         }
5641                         *q = '\0';
5642                         item = gtk_item_factory_get_item(ifactory, path);
5643                         gtk_widget_activate(item);
5644                         g_free(path);
5645                         break;
5646                 }
5647         }
5648 }
5649
5650 static void compose_set_template_menu(Compose *compose)
5651 {
5652         GSList *tmpl_list, *cur;
5653         GtkWidget *menu;
5654         GtkWidget *item;
5655
5656         tmpl_list = template_get_config();
5657
5658         menu = gtk_menu_new();
5659
5660         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
5661                 Template *tmpl = (Template *)cur->data;
5662
5663                 item = gtk_menu_item_new_with_label(tmpl->name);
5664                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
5665                 g_signal_connect(G_OBJECT(item), "activate",
5666                                  G_CALLBACK(compose_template_activate_cb),
5667                                  compose);
5668                 g_object_set_data(G_OBJECT(item), "template", tmpl);
5669                 gtk_widget_show(item);
5670         }
5671
5672         gtk_widget_show(menu);
5673         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
5674 }
5675
5676 void compose_update_actions_menu(Compose *compose)
5677 {
5678         GtkItemFactory *ifactory;
5679
5680         ifactory = gtk_item_factory_from_widget(compose->menubar);
5681         action_update_compose_menu(ifactory, "/Tools/Actions", compose);
5682 }
5683
5684 void compose_update_privacy_systems_menu(Compose *compose)
5685 {
5686         static gchar *branch_path = "/Options/Privacy System";
5687         GtkItemFactory *ifactory;
5688         GtkWidget *menuitem;
5689         GSList *systems, *cur;
5690         GList *amenu;
5691         GtkWidget *widget;
5692         GtkWidget *system_none;
5693         GSList *group;
5694
5695         ifactory = gtk_item_factory_from_widget(compose->menubar);
5696
5697         /* remove old entries */
5698         menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
5699         g_return_if_fail(menuitem != NULL);
5700
5701         amenu = GTK_MENU_SHELL(menuitem)->children->next;
5702         while (amenu != NULL) {
5703                 GList *alist = amenu->next;
5704                 gtk_widget_destroy(GTK_WIDGET(amenu->data));
5705                 amenu = alist;
5706         }
5707
5708         system_none = gtk_item_factory_get_widget(ifactory,
5709                 "/Options/Privacy System/None");
5710
5711         g_signal_connect(G_OBJECT(system_none), "activate",
5712                 G_CALLBACK(compose_set_privacy_system_cb), compose);
5713
5714         systems = privacy_get_system_ids();
5715         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
5716                 gchar *systemid = cur->data;
5717
5718                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
5719                 widget = gtk_radio_menu_item_new_with_label(group,
5720                         privacy_system_get_name(systemid));
5721                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
5722                                        g_strdup(systemid), g_free);
5723                 g_signal_connect(G_OBJECT(widget), "activate",
5724                         G_CALLBACK(compose_set_privacy_system_cb), compose);
5725
5726                 gtk_menu_append(GTK_MENU(system_none->parent), widget);
5727                 gtk_widget_show(widget);
5728                 g_free(systemid);
5729         }
5730         g_slist_free(systems);
5731 }
5732
5733 void compose_reflect_prefs_all(void)
5734 {
5735         GList *cur;
5736         Compose *compose;
5737
5738         for (cur = compose_list; cur != NULL; cur = cur->next) {
5739                 compose = (Compose *)cur->data;
5740                 compose_set_template_menu(compose);
5741         }
5742 }
5743
5744 void compose_reflect_prefs_pixmap_theme(void)
5745 {
5746         GList *cur;
5747         Compose *compose;
5748
5749         for (cur = compose_list; cur != NULL; cur = cur->next) {
5750                 compose = (Compose *)cur->data;
5751                 toolbar_update(TOOLBAR_COMPOSE, compose);
5752         }
5753 }
5754
5755 static void compose_template_apply(Compose *compose, Template *tmpl,
5756                                    gboolean replace)
5757 {
5758         GtkTextView *text;
5759         GtkTextBuffer *buffer;
5760         GtkTextMark *mark;
5761         GtkTextIter iter;
5762         gchar *qmark;
5763         gchar *parsed_str;
5764         gint cursor_pos = 0;
5765         if (!tmpl || !tmpl->value) return;
5766
5767         text = GTK_TEXT_VIEW(compose->text);
5768         buffer = gtk_text_view_get_buffer(text);
5769
5770         if (tmpl->subject && *tmpl->subject != '\0')
5771                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
5772                                    tmpl->subject);
5773         if (tmpl->to && *tmpl->to != '\0')
5774                 compose_entry_append(compose, tmpl->to, COMPOSE_TO);
5775         if (tmpl->cc && *tmpl->cc != '\0')
5776                 compose_entry_append(compose, tmpl->cc, COMPOSE_CC);
5777
5778         if (tmpl->bcc && *tmpl->bcc != '\0')
5779                 compose_entry_append(compose, tmpl->bcc, COMPOSE_BCC);
5780
5781         if (replace)
5782                 gtk_text_buffer_set_text(buffer, "", -1);
5783
5784         mark = gtk_text_buffer_get_insert(buffer);
5785         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
5786
5787         if ((compose->replyinfo == NULL) && (compose->fwdinfo == NULL)) {
5788                 parsed_str = compose_quote_fmt(compose, NULL, tmpl->value,
5789                                                NULL, NULL);
5790         } else {
5791                 if (prefs_common.quotemark && *prefs_common.quotemark)
5792                         qmark = prefs_common.quotemark;
5793                 else
5794                         qmark = "> ";
5795
5796                 if (compose->replyinfo != NULL)
5797                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
5798                                                        tmpl->value, qmark, NULL);
5799                 else if (compose->fwdinfo != NULL)
5800                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
5801                                                        tmpl->value, qmark, NULL);
5802                 else
5803                         parsed_str = NULL;
5804         }
5805
5806         if (replace && parsed_str && compose->account->auto_sig)
5807                 compose_insert_sig(compose, FALSE);
5808
5809         if (replace && parsed_str) {
5810                 gtk_text_buffer_get_start_iter(buffer, &iter);
5811                 gtk_text_buffer_place_cursor(buffer, &iter);
5812         }
5813         
5814         if (parsed_str) {
5815                 cursor_pos = quote_fmt_get_cursor_pos();
5816                 gtk_text_buffer_get_start_iter(buffer, &iter);
5817                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
5818                 gtk_text_buffer_place_cursor(buffer, &iter);
5819         }
5820
5821         if (parsed_str)
5822                 compose_changed_cb(NULL, compose);
5823 }
5824
5825 static void compose_destroy(Compose *compose)
5826 {
5827         compose_list = g_list_remove(compose_list, compose);
5828
5829         /* NOTE: address_completion_end() does nothing with the window
5830          * however this may change. */
5831         address_completion_end(compose->window);
5832
5833         slist_free_strings(compose->to_list);
5834         g_slist_free(compose->to_list);
5835         slist_free_strings(compose->newsgroup_list);
5836         g_slist_free(compose->newsgroup_list);
5837         slist_free_strings(compose->header_list);
5838         g_slist_free(compose->header_list);
5839
5840         procmsg_msginfo_free(compose->targetinfo);
5841         procmsg_msginfo_free(compose->replyinfo);
5842         procmsg_msginfo_free(compose->fwdinfo);
5843
5844         g_free(compose->replyto);
5845         g_free(compose->cc);
5846         g_free(compose->bcc);
5847         g_free(compose->newsgroups);
5848         g_free(compose->followup_to);
5849
5850         g_free(compose->ml_post);
5851
5852         g_free(compose->inreplyto);
5853         g_free(compose->references);
5854         g_free(compose->msgid);
5855         g_free(compose->boundary);
5856
5857         if (compose->redirect_filename)
5858                 g_free(compose->redirect_filename);
5859         if (compose->undostruct)
5860                 undo_destroy(compose->undostruct);
5861
5862         g_free(compose->sig_str);
5863
5864         g_free(compose->exteditor_file);
5865
5866         g_free(compose->orig_charset);
5867         if (addressbook_get_target_compose() == compose)
5868                 addressbook_set_target_compose(NULL);
5869
5870 #if USE_ASPELL
5871         if (compose->gtkaspell) {
5872                 gtkaspell_delete(compose->gtkaspell);
5873                 compose->gtkaspell = NULL;
5874         }
5875 #endif
5876
5877         prefs_common.compose_width = compose->scrolledwin->allocation.width;
5878         prefs_common.compose_height = compose->window->allocation.height;
5879
5880         if (!gtk_widget_get_parent(compose->paned))
5881                 gtk_widget_destroy(compose->paned);
5882         gtk_widget_destroy(compose->popupmenu);
5883
5884         gtk_widget_destroy(compose->window);
5885         toolbar_destroy(compose->toolbar);
5886         g_free(compose->toolbar);
5887         g_mutex_free(compose->mutex);
5888         g_free(compose);
5889 }
5890
5891 static void compose_attach_info_free(AttachInfo *ainfo)
5892 {
5893         g_free(ainfo->file);
5894         g_free(ainfo->content_type);
5895         g_free(ainfo->name);
5896         g_free(ainfo);
5897 }
5898
5899 static void compose_attach_remove_selected(Compose *compose)
5900 {
5901         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5902         GtkTreeSelection *selection;
5903         GList *sel, *cur;
5904         GtkTreeModel *model;
5905
5906         selection = gtk_tree_view_get_selection(tree_view);
5907         sel = gtk_tree_selection_get_selected_rows(selection, &model);
5908
5909         if (!sel) 
5910                 return;
5911
5912         for (cur = sel; cur != NULL; cur = cur->next) {
5913                 GtkTreePath *path = cur->data;
5914                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
5915                                                 (model, cur->data);
5916                 cur->data = ref;
5917                 gtk_tree_path_free(path);
5918         }
5919
5920         for (cur = sel; cur != NULL; cur = cur->next) {
5921                 GtkTreeRowReference *ref = cur->data;
5922                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
5923                 GtkTreeIter iter;
5924
5925                 if (gtk_tree_model_get_iter(model, &iter, path))
5926                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
5927                 
5928                 gtk_tree_path_free(path);
5929                 gtk_tree_row_reference_free(ref);
5930         }
5931
5932         g_list_free(sel);
5933 }
5934
5935 static struct _AttachProperty
5936 {
5937         GtkWidget *window;
5938         GtkWidget *mimetype_entry;
5939         GtkWidget *encoding_optmenu;
5940         GtkWidget *path_entry;
5941         GtkWidget *filename_entry;
5942         GtkWidget *ok_btn;
5943         GtkWidget *cancel_btn;
5944 } attach_prop;
5945
5946 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
5947 {       
5948         gtk_tree_path_free((GtkTreePath *)ptr);
5949 }
5950
5951 static void compose_attach_property(Compose *compose)
5952 {
5953         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5954         AttachInfo *ainfo;
5955         GtkOptionMenu *optmenu;
5956         GtkTreeSelection *selection;
5957         GList *sel;
5958         GtkTreeModel *model;
5959         GtkTreeIter iter;
5960         GtkTreePath *path;
5961         static gboolean cancelled;
5962
5963         /* only if one selected */
5964         selection = gtk_tree_view_get_selection(tree_view);
5965         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
5966                 return;
5967
5968         sel = gtk_tree_selection_get_selected_rows(selection, &model);
5969         if (!sel)
5970                 return;
5971
5972         path = (GtkTreePath *) sel->data;
5973         gtk_tree_model_get_iter(model, &iter, path);
5974         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
5975         
5976         if (!ainfo) {
5977                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
5978                 g_list_free(sel);
5979                 return;
5980         }               
5981         g_list_free(sel);
5982
5983         if (!attach_prop.window)
5984                 compose_attach_property_create(&cancelled);
5985         gtk_widget_grab_focus(attach_prop.ok_btn);
5986         gtk_widget_show(attach_prop.window);
5987         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
5988
5989         optmenu = GTK_OPTION_MENU(attach_prop.encoding_optmenu);
5990         if (ainfo->encoding == ENC_UNKNOWN)
5991                 menu_select_by_data(GTK_MENU(gtk_option_menu_get_menu(optmenu)),
5992                                     GINT_TO_POINTER(ENC_BASE64));
5993         else
5994                 menu_select_by_data(GTK_MENU(gtk_option_menu_get_menu(optmenu)),
5995                                     GINT_TO_POINTER(ainfo->encoding));
5996
5997         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
5998                            ainfo->content_type ? ainfo->content_type : "");
5999         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
6000                            ainfo->file ? ainfo->file : "");
6001         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
6002                            ainfo->name ? ainfo->name : "");
6003
6004         for (;;) {
6005                 const gchar *entry_text;
6006                 gchar *text;
6007                 gchar *cnttype = NULL;
6008                 gchar *file = NULL;
6009                 off_t size = 0;
6010                 GtkWidget *menu;
6011                 GtkWidget *menuitem;
6012
6013                 cancelled = FALSE;
6014                 gtk_main();
6015
6016                 gtk_widget_hide(attach_prop.window);
6017                 
6018                 if (cancelled) 
6019                         break;
6020
6021                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
6022                 if (*entry_text != '\0') {
6023                         gchar *p;
6024
6025                         text = g_strstrip(g_strdup(entry_text));
6026                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
6027                                 cnttype = g_strdup(text);
6028                                 g_free(text);
6029                         } else {
6030                                 alertpanel_error(_("Invalid MIME type."));
6031                                 g_free(text);
6032                                 continue;
6033                         }
6034                 }
6035
6036                 menu = gtk_option_menu_get_menu(optmenu);
6037                 menuitem = gtk_menu_get_active(GTK_MENU(menu));
6038                 ainfo->encoding = GPOINTER_TO_INT
6039                         (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID));
6040
6041                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
6042                 if (*entry_text != '\0') {
6043                         if (is_file_exist(entry_text) &&
6044                             (size = get_file_size(entry_text)) > 0)
6045                                 file = g_strdup(entry_text);
6046                         else {
6047                                 alertpanel_error
6048                                         (_("File doesn't exist or is empty."));
6049                                 g_free(cnttype);
6050                                 continue;
6051                         }
6052                 }
6053
6054                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
6055                 if (*entry_text != '\0') {
6056                         g_free(ainfo->name);
6057                         ainfo->name = g_strdup(entry_text);
6058                 }
6059
6060                 if (cnttype) {
6061                         g_free(ainfo->content_type);
6062                         ainfo->content_type = cnttype;
6063                 }
6064                 if (file) {
6065                         g_free(ainfo->file);
6066                         ainfo->file = file;
6067                 }
6068                 if (size)
6069                         ainfo->size = size;
6070
6071                 /* update tree store */
6072                 text = to_human_readable(ainfo->size);
6073                 gtk_tree_model_get_iter(model, &iter, path);
6074                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
6075                                    COL_MIMETYPE, ainfo->content_type,
6076                                    COL_SIZE, text,
6077                                    COL_NAME, ainfo->name,
6078                                    -1);
6079                 
6080                 break;
6081         }
6082
6083         gtk_tree_path_free(path);
6084 }
6085
6086 #define SET_LABEL_AND_ENTRY(str, entry, top) \
6087 { \
6088         label = gtk_label_new(str); \
6089         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
6090                          GTK_FILL, 0, 0, 0); \
6091         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
6092  \
6093         entry = gtk_entry_new(); \
6094         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
6095                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
6096 }
6097
6098 static void compose_attach_property_create(gboolean *cancelled)
6099 {
6100         GtkWidget *window;
6101         GtkWidget *vbox;
6102         GtkWidget *table;
6103         GtkWidget *label;
6104         GtkWidget *mimetype_entry;
6105         GtkWidget *hbox;
6106         GtkWidget *optmenu;
6107         GtkWidget *optmenu_menu;
6108         GtkWidget *menuitem;
6109         GtkWidget *path_entry;
6110         GtkWidget *filename_entry;
6111         GtkWidget *hbbox;
6112         GtkWidget *ok_btn;
6113         GtkWidget *cancel_btn;
6114         GList     *mime_type_list, *strlist;
6115
6116         debug_print("Creating attach_property window...\n");
6117
6118         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
6119         gtk_widget_set_size_request(window, 480, -1);
6120         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
6121         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
6122         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
6123         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
6124         g_signal_connect(G_OBJECT(window), "delete_event",
6125                          G_CALLBACK(attach_property_delete_event),
6126                          cancelled);
6127         g_signal_connect(G_OBJECT(window), "key_press_event",
6128                          G_CALLBACK(attach_property_key_pressed),
6129                          cancelled);
6130
6131         vbox = gtk_vbox_new(FALSE, 8);
6132         gtk_container_add(GTK_CONTAINER(window), vbox);
6133
6134         table = gtk_table_new(4, 2, FALSE);
6135         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
6136         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
6137         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
6138
6139         label = gtk_label_new(_("MIME type")); 
6140         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
6141                          GTK_FILL, 0, 0, 0); 
6142         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
6143         mimetype_entry = gtk_combo_new(); 
6144         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
6145                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
6146                          
6147         /* stuff with list */
6148         mime_type_list = procmime_get_mime_type_list();
6149         strlist = NULL;
6150         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
6151                 MimeType *type = (MimeType *) mime_type_list->data;
6152                 strlist = g_list_append(strlist, 
6153                                 g_strdup_printf("%s/%s",
6154                                         type->type, type->sub_type));
6155         }
6156         
6157         gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry), strlist);
6158
6159         for (mime_type_list = strlist; mime_type_list != NULL; 
6160                 mime_type_list = mime_type_list->next)
6161                 g_free(mime_type_list->data);
6162         g_list_free(strlist);
6163                          
6164         mimetype_entry = GTK_COMBO(mimetype_entry)->entry;                       
6165
6166         label = gtk_label_new(_("Encoding"));
6167         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
6168                          GTK_FILL, 0, 0, 0);
6169         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
6170
6171         hbox = gtk_hbox_new(FALSE, 0);
6172         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
6173                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
6174
6175         optmenu = gtk_option_menu_new();
6176         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
6177
6178         optmenu_menu = gtk_menu_new();
6179         MENUITEM_ADD(optmenu_menu, menuitem, "7bit", ENC_7BIT);
6180         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
6181         MENUITEM_ADD(optmenu_menu, menuitem, "8bit", ENC_8BIT);
6182         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
6183         MENUITEM_ADD(optmenu_menu, menuitem, "quoted-printable",
6184                      ENC_QUOTED_PRINTABLE);
6185         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
6186
6187         MENUITEM_ADD(optmenu_menu, menuitem, "base64", ENC_BASE64);
6188
6189         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
6190
6191         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
6192         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
6193
6194         gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK,
6195                                       &cancel_btn, GTK_STOCK_CANCEL,
6196                                       NULL, NULL);
6197         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
6198         gtk_widget_grab_default(ok_btn);
6199
6200         g_signal_connect(G_OBJECT(ok_btn), "clicked",
6201                          G_CALLBACK(attach_property_ok),
6202                          cancelled);
6203         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
6204                          G_CALLBACK(attach_property_cancel),
6205                          cancelled);
6206
6207         gtk_widget_show_all(vbox);
6208
6209         attach_prop.window           = window;
6210         attach_prop.mimetype_entry   = mimetype_entry;
6211         attach_prop.encoding_optmenu = optmenu;
6212         attach_prop.path_entry       = path_entry;
6213         attach_prop.filename_entry   = filename_entry;
6214         attach_prop.ok_btn           = ok_btn;
6215         attach_prop.cancel_btn       = cancel_btn;
6216 }
6217
6218 #undef SET_LABEL_AND_ENTRY
6219
6220 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
6221 {
6222         *cancelled = FALSE;
6223         gtk_main_quit();
6224 }
6225
6226 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
6227 {
6228         *cancelled = TRUE;
6229         gtk_main_quit();
6230 }
6231
6232 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
6233                                          gboolean *cancelled)
6234 {
6235         *cancelled = TRUE;
6236         gtk_main_quit();
6237
6238         return TRUE;
6239 }
6240
6241 static gboolean attach_property_key_pressed(GtkWidget *widget,
6242                                             GdkEventKey *event,
6243                                             gboolean *cancelled)
6244 {
6245         if (event && event->keyval == GDK_Escape) {
6246                 *cancelled = TRUE;
6247                 gtk_main_quit();
6248         }
6249         return FALSE;
6250 }
6251
6252 static void compose_exec_ext_editor(Compose *compose)
6253 {
6254 #ifdef G_OS_UNIX
6255         gchar *tmp;
6256         pid_t pid;
6257         gint pipe_fds[2];
6258
6259         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
6260                               G_DIR_SEPARATOR, compose);
6261
6262         if (pipe(pipe_fds) < 0) {
6263                 perror("pipe");
6264                 g_free(tmp);
6265                 return;
6266         }
6267
6268         if ((pid = fork()) < 0) {
6269                 perror("fork");
6270                 g_free(tmp);
6271                 return;
6272         }
6273
6274         if (pid != 0) {
6275                 /* close the write side of the pipe */
6276                 close(pipe_fds[1]);
6277
6278                 compose->exteditor_file    = g_strdup(tmp);
6279                 compose->exteditor_pid     = pid;
6280
6281                 compose_set_ext_editor_sensitive(compose, FALSE);
6282
6283                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
6284                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
6285                                                         G_IO_IN,
6286                                                         compose_input_cb,
6287                                                         compose);
6288         } else {        /* process-monitoring process */
6289                 pid_t pid_ed;
6290
6291                 if (setpgid(0, 0))
6292                         perror("setpgid");
6293
6294                 /* close the read side of the pipe */
6295                 close(pipe_fds[0]);
6296
6297                 if (compose_write_body_to_file(compose, tmp) < 0) {
6298                         fd_write_all(pipe_fds[1], "2\n", 2);
6299                         _exit(1);
6300                 }
6301
6302                 pid_ed = compose_exec_ext_editor_real(tmp);
6303                 if (pid_ed < 0) {
6304                         fd_write_all(pipe_fds[1], "1\n", 2);
6305                         _exit(1);
6306                 }
6307
6308                 /* wait until editor is terminated */
6309                 waitpid(pid_ed, NULL, 0);
6310
6311                 fd_write_all(pipe_fds[1], "0\n", 2);
6312
6313                 close(pipe_fds[1]);
6314                 _exit(0);
6315         }
6316
6317         g_free(tmp);
6318 #endif /* G_OS_UNIX */
6319 }
6320
6321 #ifdef G_OS_UNIX
6322 static gint compose_exec_ext_editor_real(const gchar *file)
6323 {
6324         static gchar *def_cmd = "emacs %s";
6325         gchar buf[1024];
6326         gchar *p;
6327         gchar **cmdline;
6328         pid_t pid;
6329
6330         g_return_val_if_fail(file != NULL, -1);
6331
6332         if ((pid = fork()) < 0) {
6333                 perror("fork");
6334                 return -1;
6335         }
6336
6337         if (pid != 0) return pid;
6338
6339         /* grandchild process */
6340
6341         if (setpgid(0, getppid()))
6342                 perror("setpgid");
6343
6344         if (prefs_common.ext_editor_cmd &&
6345             (p = strchr(prefs_common.ext_editor_cmd, '%')) &&
6346             *(p + 1) == 's' && !strchr(p + 2, '%')) {
6347                 g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, file);
6348         } else {
6349                 if (prefs_common.ext_editor_cmd)
6350                         g_warning("External editor command line is invalid: '%s'\n",
6351                                   prefs_common.ext_editor_cmd);
6352                 g_snprintf(buf, sizeof(buf), def_cmd, file);
6353         }
6354
6355         cmdline = strsplit_with_quote(buf, " ", 1024);
6356         execvp(cmdline[0], cmdline);
6357
6358         perror("execvp");
6359         g_strfreev(cmdline);
6360
6361         _exit(1);
6362 }
6363
6364 static gboolean compose_ext_editor_kill(Compose *compose)
6365 {
6366         pid_t pgid = compose->exteditor_pid * -1;
6367         gint ret;
6368
6369         ret = kill(pgid, 0);
6370
6371         if (ret == 0 || (ret == -1 && EPERM == errno)) {
6372                 AlertValue val;
6373                 gchar *msg;
6374
6375                 msg = g_strdup_printf
6376                         (_("The external editor is still working.\n"
6377                            "Force terminating the process?\n"
6378                            "process group id: %d"), -pgid);
6379                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_YES, GTK_STOCK_NO,
6380                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
6381                         
6382                 g_free(msg);
6383
6384                 if (val == G_ALERTDEFAULT) {
6385                         g_source_remove(compose->exteditor_tag);
6386                         g_io_channel_shutdown(compose->exteditor_ch,
6387                                               FALSE, NULL);
6388                         g_io_channel_unref(compose->exteditor_ch);
6389
6390                         if (kill(pgid, SIGTERM) < 0) perror("kill");
6391                         waitpid(compose->exteditor_pid, NULL, 0);
6392
6393                         g_warning("Terminated process group id: %d", -pgid);
6394                         g_warning("Temporary file: %s",
6395                                   compose->exteditor_file);
6396
6397                         compose_set_ext_editor_sensitive(compose, TRUE);
6398
6399                         g_free(compose->exteditor_file);
6400                         compose->exteditor_file    = NULL;
6401                         compose->exteditor_pid     = -1;
6402                         compose->exteditor_ch      = NULL;
6403                         compose->exteditor_tag     = -1;
6404                 } else
6405                         return FALSE;
6406         }
6407
6408         return TRUE;
6409 }
6410
6411 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
6412                                  gpointer data)
6413 {
6414         gchar buf[3] = "3";
6415         Compose *compose = (Compose *)data;
6416         gsize bytes_read;
6417
6418         debug_print(_("Compose: input from monitoring process\n"));
6419
6420         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
6421
6422         g_io_channel_shutdown(source, FALSE, NULL);
6423         g_io_channel_unref(source);
6424
6425         waitpid(compose->exteditor_pid, NULL, 0);
6426
6427         if (buf[0] == '0') {            /* success */
6428                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
6429                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
6430
6431                 gtk_text_buffer_set_text(buffer, "", -1);
6432                 compose_insert_file(compose, compose->exteditor_file);
6433                 compose_changed_cb(NULL, compose);
6434
6435                 if (g_unlink(compose->exteditor_file) < 0)
6436                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
6437         } else if (buf[0] == '1') {     /* failed */
6438                 g_warning("Couldn't exec external editor\n");
6439                 if (g_unlink(compose->exteditor_file) < 0)
6440                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
6441         } else if (buf[0] == '2') {
6442                 g_warning("Couldn't write to file\n");
6443         } else if (buf[0] == '3') {
6444                 g_warning("Pipe read failed\n");
6445         }
6446
6447         compose_set_ext_editor_sensitive(compose, TRUE);
6448
6449         g_free(compose->exteditor_file);
6450         compose->exteditor_file    = NULL;
6451         compose->exteditor_pid     = -1;
6452         compose->exteditor_ch      = NULL;
6453         compose->exteditor_tag     = -1;
6454
6455         return FALSE;
6456 }
6457
6458 static void compose_set_ext_editor_sensitive(Compose *compose,
6459                                              gboolean sensitive)
6460 {
6461         GtkItemFactory *ifactory;
6462
6463         ifactory = gtk_item_factory_from_widget(compose->menubar);
6464
6465         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
6466         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
6467         menu_set_sensitive(ifactory, "/Message/Insert file", sensitive);
6468         menu_set_sensitive(ifactory, "/Message/Insert signature", sensitive);
6469         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
6470         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
6471         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
6472                            sensitive);
6473
6474         gtk_widget_set_sensitive(compose->text,                   sensitive);
6475         gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
6476         gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
6477         gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
6478         gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
6479         gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
6480         gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
6481         gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn,  sensitive);
6482         gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn,  sensitive);
6483 }
6484 #endif /* G_OS_UNIX */
6485
6486 /**
6487  * compose_undo_state_changed:
6488  *
6489  * Change the sensivity of the menuentries undo and redo
6490  **/
6491 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
6492                                        gint redo_state, gpointer data)
6493 {
6494         GtkWidget *widget = GTK_WIDGET(data);
6495         GtkItemFactory *ifactory;
6496
6497         g_return_if_fail(widget != NULL);
6498
6499         ifactory = gtk_item_factory_from_widget(widget);
6500
6501         switch (undo_state) {
6502         case UNDO_STATE_TRUE:
6503                 if (!undostruct->undo_state) {
6504                         undostruct->undo_state = TRUE;
6505                         menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
6506                 }
6507                 break;
6508         case UNDO_STATE_FALSE:
6509                 if (undostruct->undo_state) {
6510                         undostruct->undo_state = FALSE;
6511                         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
6512                 }
6513                 break;
6514         case UNDO_STATE_UNCHANGED:
6515                 break;
6516         case UNDO_STATE_REFRESH:
6517                 menu_set_sensitive(ifactory, "/Edit/Undo",
6518                                    undostruct->undo_state);
6519                 break;
6520         default:
6521                 g_warning("Undo state not recognized");
6522                 break;
6523         }
6524
6525         switch (redo_state) {
6526         case UNDO_STATE_TRUE:
6527                 if (!undostruct->redo_state) {
6528                         undostruct->redo_state = TRUE;
6529                         menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
6530                 }
6531                 break;
6532         case UNDO_STATE_FALSE:
6533                 if (undostruct->redo_state) {
6534                         undostruct->redo_state = FALSE;
6535                         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
6536                 }
6537                 break;
6538         case UNDO_STATE_UNCHANGED:
6539                 break;
6540         case UNDO_STATE_REFRESH:
6541                 menu_set_sensitive(ifactory, "/Edit/Redo",
6542                                    undostruct->redo_state);
6543                 break;
6544         default:
6545                 g_warning("Redo state not recognized");
6546                 break;
6547         }
6548 }
6549
6550 /* callback functions */
6551
6552 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
6553  * includes "non-client" (windows-izm) in calculation, so this calculation
6554  * may not be accurate.
6555  */
6556 static gboolean compose_edit_size_alloc(GtkEditable *widget,
6557                                         GtkAllocation *allocation,
6558                                         GtkSHRuler *shruler)
6559 {
6560         if (prefs_common.show_ruler) {
6561                 gint char_width = 0, char_height = 0;
6562                 gint line_width_in_chars;
6563
6564                 gtkut_get_font_size(GTK_WIDGET(widget),
6565                                     &char_width, &char_height);
6566                 line_width_in_chars =
6567                         (allocation->width - allocation->x) / char_width;
6568
6569                 /* got the maximum */
6570                 gtk_ruler_set_range(GTK_RULER(shruler),
6571                                     0.0, line_width_in_chars, 0,
6572                                     /*line_width_in_chars*/ char_width);
6573         }
6574
6575         return TRUE;
6576 }
6577
6578 static void account_activated(GtkMenuItem *menuitem, gpointer data)
6579 {
6580         Compose *compose = (Compose *)data;
6581
6582         PrefsAccount *ac;
6583         gchar *folderidentifier;
6584
6585         ac = account_find_from_id(
6586                 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID)));
6587         g_return_if_fail(ac != NULL);
6588
6589         if (ac != compose->account)
6590                 compose_select_account(compose, ac, FALSE);
6591
6592         /* Set message save folder */
6593         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6594                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
6595         }
6596         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
6597                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6598                            
6599         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
6600         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6601                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6602                                   (compose->account, F_OUTBOX));
6603                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
6604                 g_free(folderidentifier);
6605         }
6606 }
6607
6608 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
6609                             GtkTreeViewColumn *column, Compose *compose)
6610 {
6611         compose_attach_property(compose);
6612 }
6613
6614 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
6615                                       gpointer data)
6616 {
6617         Compose *compose = (Compose *)data;
6618         GtkTreeSelection *attach_selection;
6619         gint attach_nr_selected;
6620         GtkItemFactory *ifactory;
6621         
6622         if (!event) return FALSE;
6623
6624         if (event->button == 3) {
6625                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
6626                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
6627                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
6628                         
6629                 if (attach_nr_selected > 0)
6630                 {
6631                         menu_set_sensitive(ifactory, "/Remove", TRUE);
6632                         menu_set_sensitive(ifactory, "/Properties...", TRUE);
6633                 } else {
6634                         menu_set_sensitive(ifactory, "/Remove", FALSE);
6635                         menu_set_sensitive(ifactory, "/Properties...", FALSE);
6636                 }
6637                         
6638                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
6639                                NULL, NULL, event->button, event->time);
6640                 return TRUE;                           
6641         }
6642
6643         return FALSE;
6644 }
6645
6646 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
6647                                    gpointer data)
6648 {
6649         Compose *compose = (Compose *)data;
6650
6651         if (!event) return FALSE;
6652
6653         switch (event->keyval) {
6654         case GDK_Delete:
6655                 compose_attach_remove_selected(compose);
6656                 break;
6657         }
6658         return FALSE;
6659 }
6660
6661 static void compose_allow_user_actions (Compose *compose, gboolean allow)
6662 {
6663         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
6664         toolbar_comp_set_sensitive(compose, allow);
6665         menu_set_sensitive(ifactory, "/Message", allow);
6666         menu_set_sensitive(ifactory, "/Edit", allow);
6667 #if USE_ASPELL
6668         menu_set_sensitive(ifactory, "/Spelling", allow);
6669 #endif  
6670         menu_set_sensitive(ifactory, "/Options", allow);
6671         menu_set_sensitive(ifactory, "/Tools", allow);
6672         menu_set_sensitive(ifactory, "/Help", allow);
6673         
6674         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
6675
6676 }
6677
6678 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
6679 {
6680         Compose *compose = (Compose *)data;
6681         
6682         if (prefs_common.work_offline && !inc_offline_should_override())
6683                 return;
6684         
6685         if (compose->draft_timeout_tag != -1) { /* CLAWS: disable draft timeout */
6686                 gtk_timeout_remove(compose->draft_timeout_tag);
6687                 compose->draft_timeout_tag = -1;
6688         }
6689
6690         compose_send(compose);
6691 }
6692
6693 static void compose_send_later_cb(gpointer data, guint action,
6694                                   GtkWidget *widget)
6695 {
6696         Compose *compose = (Compose *)data;
6697         gint val;
6698
6699         val = compose_queue_sub(compose, NULL, NULL, TRUE);
6700         if (!val) 
6701                 compose_close(compose);
6702         else if (val == -2) {
6703                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
6704         }
6705 }
6706
6707 void compose_draft (gpointer data) 
6708 {
6709         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);     
6710 }
6711
6712 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
6713 {
6714         Compose *compose = (Compose *)data;
6715         FolderItem *draft;
6716         gchar *tmp;
6717         gint msgnum;
6718         MsgFlags flag = {0, 0};
6719         static gboolean lock = FALSE;
6720         MsgInfo *newmsginfo;
6721         FILE *fp;
6722         
6723         if (lock) return;
6724
6725         draft = account_get_special_folder(compose->account, F_DRAFT);
6726         g_return_if_fail(draft != NULL);
6727         
6728         if (!g_mutex_trylock(compose->mutex)) {
6729                 /* we don't want to lock the mutex once it's available,
6730                  * because as the only other part of compose.c locking
6731                  * it is compose_close - which means once unlocked,
6732                  * the compose struct will be freed */
6733                 debug_print("couldn't lock mutex, probably sending\n");
6734                 return;
6735         }
6736         
6737         lock = TRUE;
6738
6739         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
6740                               G_DIR_SEPARATOR, compose);
6741         if ((fp = g_fopen(tmp, "wb")) == NULL) {
6742                 FILE_OP_ERROR(tmp, "fopen");
6743                 goto unlock;
6744         }
6745
6746         /* chmod for security */
6747         if (change_file_mode_rw(fp, tmp) < 0) {
6748                 FILE_OP_ERROR(tmp, "chmod");
6749                 g_warning("can't change file mode\n");
6750         }
6751
6752         /* Save draft infos */
6753         fprintf(fp, "X-Sylpheed-Account-Id:%d\n", compose->account->account_id);
6754         fprintf(fp, "S:%s\n", compose->account->address);
6755         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6756                 gchar *savefolderid;
6757
6758                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
6759                 fprintf(fp, "SCF:%s\n", savefolderid);
6760                 g_free(savefolderid);
6761         }
6762         if (compose->privacy_system) {
6763                 fprintf(fp, "X-Sylpheed-Sign:%d\n", compose->use_signing);
6764                 fprintf(fp, "X-Sylpheed-Encrypt:%d\n", compose->use_encryption);
6765                 fprintf(fp, "X-Sylpheed-Privacy-System:%s\n", compose->privacy_system);
6766         }
6767         fprintf(fp, "\n");
6768
6769         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE) < 0) {
6770                 fclose(fp);
6771                 g_unlink(tmp);
6772                 g_free(tmp);
6773                 goto unlock;
6774         }
6775         fclose(fp);
6776
6777         folder_item_scan(draft);
6778         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
6779                 g_unlink(tmp);
6780                 g_free(tmp);
6781                 if (action != COMPOSE_AUTO_SAVE)
6782                         alertpanel_error(_("Could not save draft."));
6783                 goto unlock;
6784         }
6785         g_free(tmp);
6786         draft->mtime = 0;       /* force updating */
6787
6788         if (compose->mode == COMPOSE_REEDIT) {
6789                 compose_remove_reedit_target(compose);
6790         }
6791
6792         newmsginfo = folder_item_get_msginfo(draft, msgnum);
6793         if (newmsginfo) {
6794                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
6795                 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
6796                 if (compose_use_attach(compose))
6797                         procmsg_msginfo_set_flags(newmsginfo, 0,
6798                                                   MSG_HAS_ATTACHMENT);
6799
6800                 procmsg_msginfo_free(newmsginfo);
6801         }
6802         
6803         folder_item_scan(draft);
6804         
6805         if (action == COMPOSE_QUIT_EDITING) {
6806                 lock = FALSE;
6807                 g_mutex_unlock(compose->mutex); /* must be done before closing */
6808                 compose_close(compose);
6809                 return;
6810         } else {
6811                 struct stat s;
6812                 gchar *path;
6813
6814                 path = folder_item_fetch_msg(draft, msgnum);
6815                 g_return_if_fail(path != NULL);
6816                 if (g_stat(path, &s) < 0) {
6817                         FILE_OP_ERROR(path, "stat");
6818                         g_free(path);
6819                         goto unlock;
6820                 }
6821                 g_free(path);
6822
6823                 procmsg_msginfo_free(compose->targetinfo);
6824                 compose->targetinfo = procmsg_msginfo_new();
6825                 compose->targetinfo->msgnum = msgnum;
6826                 compose->targetinfo->size = s.st_size;
6827                 compose->targetinfo->mtime = s.st_mtime;
6828                 compose->targetinfo->folder = draft;
6829                 compose->mode = COMPOSE_REEDIT;
6830                 
6831                 if (action == COMPOSE_AUTO_SAVE) {
6832                         compose->autosaved_draft = compose->targetinfo;
6833                 }
6834                 compose->modified = FALSE;
6835                 compose_set_title(compose);
6836         }
6837 unlock:
6838         lock = FALSE;
6839         g_mutex_unlock(compose->mutex);
6840 }
6841
6842 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
6843 {
6844         Compose *compose = (Compose *)data;
6845         GList *file_list;
6846
6847         if (compose->redirect_filename != NULL)
6848                 return;
6849
6850         file_list = filesel_select_multiple_files_open(_("Select file"));
6851
6852         if (file_list) {
6853                 GList *tmp;
6854
6855                 for ( tmp = file_list; tmp; tmp = tmp->next) {
6856                         gchar *file = (gchar *) tmp->data;
6857                         gchar *utf8_filename = conv_filename_to_utf8(file);
6858                         compose_attach_append(compose, file, utf8_filename, NULL);
6859                         compose_changed_cb(NULL, compose);
6860                         g_free(file);
6861                         g_free(utf8_filename);
6862                 }
6863                 g_list_free(file_list);
6864         }               
6865 }
6866
6867 static void compose_insert_file_cb(gpointer data, guint action,
6868                                    GtkWidget *widget)
6869 {
6870         Compose *compose = (Compose *)data;
6871         GList *file_list;
6872
6873         file_list = filesel_select_multiple_files_open(_("Select file"));
6874
6875         if (file_list) {
6876                 GList *tmp;
6877
6878                 for ( tmp = file_list; tmp; tmp = tmp->next) {
6879                         gchar *file = (gchar *) tmp->data;
6880                         gchar *filedup = g_strdup(file);
6881                         gchar *shortfile = g_path_get_basename(filedup);
6882                         ComposeInsertResult res;
6883
6884                         res = compose_insert_file(compose, file);
6885                         if (res == COMPOSE_INSERT_READ_ERROR) {
6886                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
6887                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
6888                                 alertpanel_error(_("File '%s' contained invalid characters\n"
6889                                                    "for the current encoding, insertion may be incorrect."), shortfile);
6890                         }
6891                         g_free(shortfile);
6892                         g_free(filedup);
6893                         g_free(file);
6894                 }
6895                 g_list_free(file_list);
6896         }
6897 }
6898
6899 static void compose_insert_sig_cb(gpointer data, guint action,
6900                                   GtkWidget *widget)
6901 {
6902         Compose *compose = (Compose *)data;
6903
6904         compose_insert_sig(compose, FALSE);
6905 }
6906
6907 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
6908                               gpointer data)
6909 {
6910         gint x, y;
6911         Compose *compose = (Compose *)data;
6912
6913         gtkut_widget_get_uposition(widget, &x, &y);
6914         prefs_common.compose_x = x;
6915         prefs_common.compose_y = y;
6916
6917         if (compose->sending)
6918                 return TRUE;
6919         compose_close_cb(compose, 0, NULL);
6920         return TRUE;
6921 }
6922
6923 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
6924 {
6925         Compose *compose = (Compose *)data;
6926         AlertValue val;
6927
6928 #ifdef G_OS_UNIX
6929         if (compose->exteditor_tag != -1) {
6930                 if (!compose_ext_editor_kill(compose))
6931                         return;
6932         }
6933 #endif
6934
6935         if (compose->modified) {
6936                 val = alertpanel(_("Discard message"),
6937                                  _("This message has been modified. Discard it?"),
6938                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
6939
6940                 switch (val) {
6941                 case G_ALERTDEFAULT:
6942                         if (prefs_common.autosave)
6943                                 compose_remove_draft(compose);                  
6944                         break;
6945                 case G_ALERTALTERNATE:
6946                         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);
6947                         return;
6948                 default:
6949                         return;
6950                 }
6951         }
6952
6953         compose_close(compose);
6954 }
6955
6956 static void compose_set_encoding_cb(gpointer data, guint action,
6957                                     GtkWidget *widget)
6958 {
6959         Compose *compose = (Compose *)data;
6960
6961         if (GTK_CHECK_MENU_ITEM(widget)->active)
6962                 compose->out_encoding = (CharSet)action;
6963 }
6964
6965 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
6966 {
6967         Compose *compose = (Compose *)data;
6968
6969         addressbook_open(compose);
6970 }
6971
6972 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
6973 {
6974         Compose *compose = (Compose *)data;
6975         Template *tmpl;
6976         gchar *msg;
6977         AlertValue val;
6978
6979         tmpl = g_object_get_data(G_OBJECT(widget), "template");
6980         g_return_if_fail(tmpl != NULL);
6981
6982         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
6983                               tmpl->name);
6984         val = alertpanel(_("Apply template"), msg,
6985                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
6986         g_free(msg);
6987
6988         if (val == G_ALERTDEFAULT)
6989                 compose_template_apply(compose, tmpl, TRUE);
6990         else if (val == G_ALERTALTERNATE)
6991                 compose_template_apply(compose, tmpl, FALSE);
6992 }
6993
6994 static void compose_ext_editor_cb(gpointer data, guint action,
6995                                   GtkWidget *widget)
6996 {
6997         Compose *compose = (Compose *)data;
6998
6999         compose_exec_ext_editor(compose);
7000 }
7001
7002 static void compose_undo_cb(Compose *compose)
7003 {
7004         gboolean prev_autowrap = compose->autowrap;
7005
7006         compose->autowrap = FALSE;
7007         undo_undo(compose->undostruct);
7008         compose->autowrap = prev_autowrap;
7009 }
7010
7011 static void compose_redo_cb(Compose *compose)
7012 {
7013         gboolean prev_autowrap = compose->autowrap;
7014         
7015         compose->autowrap = FALSE;
7016         undo_redo(compose->undostruct);
7017         compose->autowrap = prev_autowrap;
7018 }
7019
7020 static void entry_cut_clipboard(GtkWidget *entry)
7021 {
7022         if (GTK_IS_EDITABLE(entry))
7023                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
7024         else if (GTK_IS_TEXT_VIEW(entry))
7025                 gtk_text_buffer_cut_clipboard(
7026                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
7027                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
7028                         TRUE);
7029 }
7030
7031 static void entry_copy_clipboard(GtkWidget *entry)
7032 {
7033         if (GTK_IS_EDITABLE(entry))
7034                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
7035         else if (GTK_IS_TEXT_VIEW(entry))
7036                 gtk_text_buffer_copy_clipboard(
7037                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
7038                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
7039 }
7040
7041 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
7042                                   gboolean wrap, GdkAtom clip)
7043 {
7044         if (GTK_IS_TEXT_VIEW(entry)) {
7045                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
7046                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
7047                 GtkTextIter start_iter, end_iter;
7048                 gint start, end;
7049                 
7050                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
7051
7052                 if (contents == NULL)
7053                         return;
7054
7055                 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
7056                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
7057                 
7058                 start = gtk_text_iter_get_offset(&start_iter);
7059
7060                 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
7061                 
7062                 if (!wrap) {
7063                         end = start + strlen(contents);
7064                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
7065                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
7066                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
7067                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
7068                         mark_start = gtk_text_buffer_get_insert(buffer);
7069                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
7070                         gtk_text_iter_backward_char(&start_iter);
7071                         compose_beautify_paragraph(compose, &start_iter, TRUE);
7072                 }
7073                 
7074         } else if (GTK_IS_EDITABLE(entry))
7075                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
7076         
7077 }
7078
7079 static void entry_allsel(GtkWidget *entry)
7080 {
7081         if (GTK_IS_EDITABLE(entry))
7082                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
7083         else if (GTK_IS_TEXT_VIEW(entry)) {
7084                 GtkTextIter startiter, enditer;
7085                 GtkTextBuffer *textbuf;
7086
7087                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
7088                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
7089                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
7090
7091                 gtk_text_buffer_move_mark_by_name(textbuf, 
7092                         "selection_bound", &startiter);
7093                 gtk_text_buffer_move_mark_by_name(textbuf, 
7094                         "insert", &enditer);
7095         }
7096 }
7097
7098 static void compose_cut_cb(Compose *compose)
7099 {
7100         if (compose->focused_editable &&
7101             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
7102                 entry_cut_clipboard(compose->focused_editable);
7103 }
7104
7105 static void compose_copy_cb(Compose *compose)
7106 {
7107         if (compose->focused_editable &&
7108             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
7109                 entry_copy_clipboard(compose->focused_editable);
7110 }
7111
7112 static void compose_paste_cb(Compose *compose)
7113 {
7114         gint prev_autowrap;
7115         GtkTextBuffer *buffer;
7116         BLOCK_WRAP();
7117         if (compose->focused_editable &&
7118             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
7119                 entry_paste_clipboard(compose, compose->focused_editable, 
7120                                 prefs_common.linewrap_pastes,
7121                                 GDK_SELECTION_CLIPBOARD);
7122         UNBLOCK_WRAP();
7123 }
7124
7125 static void compose_paste_as_quote_cb(Compose *compose)
7126 {
7127         gint wrap_quote = prefs_common.linewrap_quote;
7128         if (compose->focused_editable &&
7129             GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) {
7130                 /* let text_insert() (called directly or at a later time
7131                  * after the gtk_editable_paste_clipboard) know that 
7132                  * text is to be inserted as a quotation. implemented
7133                  * by using a simple refcount... */
7134                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
7135                                                 G_OBJECT(compose->focused_editable),
7136                                                 "paste_as_quotation"));
7137                 g_object_set_data(G_OBJECT(compose->focused_editable),
7138                                     "paste_as_quotation",
7139                                     GINT_TO_POINTER(paste_as_quotation + 1));
7140                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
7141                 entry_paste_clipboard(compose, compose->focused_editable, 
7142                                 prefs_common.linewrap_pastes,
7143                                 GDK_SELECTION_CLIPBOARD);
7144                 prefs_common.linewrap_quote = wrap_quote;
7145         }
7146 }
7147
7148 static void compose_paste_no_wrap_cb(Compose *compose)
7149 {
7150         gint prev_autowrap;
7151         GtkTextBuffer *buffer;
7152         BLOCK_WRAP();
7153         if (compose->focused_editable &&
7154             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
7155                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
7156                         GDK_SELECTION_CLIPBOARD);
7157         UNBLOCK_WRAP();
7158 }
7159
7160 static void compose_paste_wrap_cb(Compose *compose)
7161 {
7162         gint prev_autowrap;
7163         GtkTextBuffer *buffer;
7164         BLOCK_WRAP();
7165         if (compose->focused_editable &&
7166             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
7167                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
7168                         GDK_SELECTION_CLIPBOARD);
7169         UNBLOCK_WRAP();
7170 }
7171
7172 static void compose_allsel_cb(Compose *compose)
7173 {
7174         if (compose->focused_editable &&
7175             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
7176                 entry_allsel(compose->focused_editable);
7177 }
7178
7179 static void textview_move_beginning_of_line (GtkTextView *text)
7180 {
7181         GtkTextBuffer *buffer;
7182         GtkTextMark *mark;
7183         GtkTextIter ins;
7184
7185         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7186
7187         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7188         mark = gtk_text_buffer_get_insert(buffer);
7189         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7190         gtk_text_iter_set_line_offset(&ins, 0);
7191         gtk_text_buffer_place_cursor(buffer, &ins);
7192 }
7193
7194 static void textview_move_forward_character (GtkTextView *text)
7195 {
7196         GtkTextBuffer *buffer;
7197         GtkTextMark *mark;
7198         GtkTextIter ins;
7199
7200         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7201
7202         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7203         mark = gtk_text_buffer_get_insert(buffer);
7204         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7205         if (gtk_text_iter_forward_cursor_position(&ins))
7206                 gtk_text_buffer_place_cursor(buffer, &ins);
7207 }
7208
7209 static void textview_move_backward_character (GtkTextView *text)
7210 {
7211         GtkTextBuffer *buffer;
7212         GtkTextMark *mark;
7213         GtkTextIter ins;
7214
7215         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7216
7217         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7218         mark = gtk_text_buffer_get_insert(buffer);
7219         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7220         if (gtk_text_iter_backward_cursor_position(&ins))
7221                 gtk_text_buffer_place_cursor(buffer, &ins);
7222 }
7223
7224 static void textview_move_forward_word (GtkTextView *text)
7225 {
7226         GtkTextBuffer *buffer;
7227         GtkTextMark *mark;
7228         GtkTextIter ins;
7229         gint count;
7230
7231         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7232
7233         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7234         mark = gtk_text_buffer_get_insert(buffer);
7235         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7236         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
7237         if (gtk_text_iter_forward_word_ends(&ins, count)) {
7238                 gtk_text_iter_backward_word_start(&ins);
7239                 gtk_text_buffer_place_cursor(buffer, &ins);
7240         }
7241 }
7242
7243 static void textview_move_backward_word (GtkTextView *text)
7244 {
7245         GtkTextBuffer *buffer;
7246         GtkTextMark *mark;
7247         GtkTextIter ins;
7248         gint count;
7249
7250         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7251
7252         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7253         mark = gtk_text_buffer_get_insert(buffer);
7254         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7255         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
7256         if (gtk_text_iter_backward_word_starts(&ins, 1))
7257                 gtk_text_buffer_place_cursor(buffer, &ins);
7258 }
7259
7260 static void textview_move_end_of_line (GtkTextView *text)
7261 {
7262         GtkTextBuffer *buffer;
7263         GtkTextMark *mark;
7264         GtkTextIter ins;
7265
7266         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7267
7268         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7269         mark = gtk_text_buffer_get_insert(buffer);
7270         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7271         if (gtk_text_iter_forward_to_line_end(&ins))
7272                 gtk_text_buffer_place_cursor(buffer, &ins);
7273 }
7274
7275 static void textview_move_next_line (GtkTextView *text)
7276 {
7277         GtkTextBuffer *buffer;
7278         GtkTextMark *mark;
7279         GtkTextIter ins;
7280         gint offset;
7281
7282         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7283
7284         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7285         mark = gtk_text_buffer_get_insert(buffer);
7286         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7287         offset = gtk_text_iter_get_line_offset(&ins);
7288         if (gtk_text_iter_forward_line(&ins)) {
7289                 gtk_text_iter_set_line_offset(&ins, offset);
7290                 gtk_text_buffer_place_cursor(buffer, &ins);
7291         }
7292 }
7293
7294 static void textview_move_previous_line (GtkTextView *text)
7295 {
7296         GtkTextBuffer *buffer;
7297         GtkTextMark *mark;
7298         GtkTextIter ins;
7299         gint offset;
7300
7301         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7302
7303         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7304         mark = gtk_text_buffer_get_insert(buffer);
7305         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7306         offset = gtk_text_iter_get_line_offset(&ins);
7307         if (gtk_text_iter_backward_line(&ins)) {
7308                 gtk_text_iter_set_line_offset(&ins, offset);
7309                 gtk_text_buffer_place_cursor(buffer, &ins);
7310         }
7311 }
7312
7313 static void textview_delete_forward_character (GtkTextView *text)
7314 {
7315         GtkTextBuffer *buffer;
7316         GtkTextMark *mark;
7317         GtkTextIter ins, end_iter;
7318
7319         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7320
7321         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7322         mark = gtk_text_buffer_get_insert(buffer);
7323         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7324         end_iter = ins;
7325         if (gtk_text_iter_forward_char(&end_iter)) {
7326                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
7327         }
7328 }
7329
7330 static void textview_delete_backward_character (GtkTextView *text)
7331 {
7332         GtkTextBuffer *buffer;
7333         GtkTextMark *mark;
7334         GtkTextIter ins, end_iter;
7335
7336         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7337
7338         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7339         mark = gtk_text_buffer_get_insert(buffer);
7340         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7341         end_iter = ins;
7342         if (gtk_text_iter_backward_char(&end_iter)) {
7343                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
7344         }
7345 }
7346
7347 static void textview_delete_forward_word (GtkTextView *text)
7348 {
7349         GtkTextBuffer *buffer;
7350         GtkTextMark *mark;
7351         GtkTextIter ins, end_iter;
7352
7353         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7354
7355         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7356         mark = gtk_text_buffer_get_insert(buffer);
7357         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7358         end_iter = ins;
7359         if (gtk_text_iter_forward_word_end(&end_iter)) {
7360                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
7361         }
7362 }
7363
7364 static void textview_delete_backward_word (GtkTextView *text)
7365 {
7366         GtkTextBuffer *buffer;
7367         GtkTextMark *mark;
7368         GtkTextIter ins, end_iter;
7369
7370         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7371
7372         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7373         mark = gtk_text_buffer_get_insert(buffer);
7374         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7375         end_iter = ins;
7376         if (gtk_text_iter_backward_word_start(&end_iter)) {
7377                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
7378         }
7379 }
7380
7381 static void textview_delete_line (GtkTextView *text)
7382 {
7383         GtkTextBuffer *buffer;
7384         GtkTextMark *mark;
7385         GtkTextIter ins, start_iter, end_iter;
7386         gboolean found;
7387
7388         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7389
7390         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7391         mark = gtk_text_buffer_get_insert(buffer);
7392         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7393
7394         start_iter = ins;
7395         gtk_text_iter_set_line_offset(&start_iter, 0);
7396
7397         end_iter = ins;
7398         if (gtk_text_iter_ends_line(&end_iter))
7399                 found = gtk_text_iter_forward_char(&end_iter);
7400         else
7401                 found = gtk_text_iter_forward_to_line_end(&end_iter);
7402
7403         if (found)
7404                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
7405 }
7406
7407 static void textview_delete_to_line_end (GtkTextView *text)
7408 {
7409         GtkTextBuffer *buffer;
7410         GtkTextMark *mark;
7411         GtkTextIter ins, end_iter;
7412         gboolean found;
7413
7414         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
7415
7416         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7417         mark = gtk_text_buffer_get_insert(buffer);
7418         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
7419         end_iter = ins;
7420         if (gtk_text_iter_ends_line(&end_iter))
7421                 found = gtk_text_iter_forward_char(&end_iter);
7422         else
7423                 found = gtk_text_iter_forward_to_line_end(&end_iter);
7424         if (found)
7425                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
7426 }
7427
7428 static void compose_advanced_action_cb(Compose *compose,
7429                                         ComposeCallAdvancedAction action)
7430 {
7431         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
7432         static struct {
7433                 void (*do_action) (GtkTextView *text);
7434         } action_table[] = {
7435                 {textview_move_beginning_of_line},
7436                 {textview_move_forward_character},
7437                 {textview_move_backward_character},
7438                 {textview_move_forward_word},
7439                 {textview_move_backward_word},
7440                 {textview_move_end_of_line},
7441                 {textview_move_next_line},
7442                 {textview_move_previous_line},
7443                 {textview_delete_forward_character},
7444                 {textview_delete_backward_character},
7445                 {textview_delete_forward_word},
7446                 {textview_delete_backward_word},
7447                 {textview_delete_line},
7448                 {NULL}, /* gtk_stext_delete_line_n */
7449                 {textview_delete_to_line_end}
7450         };
7451
7452         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
7453
7454         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
7455             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
7456                 if (action_table[action].do_action)
7457                         action_table[action].do_action(text);
7458                 else
7459                         g_warning("Not implemented yet.");
7460         }
7461 }
7462
7463 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
7464 {
7465         gchar *str = NULL;
7466         
7467         if (GTK_IS_EDITABLE(widget)) {
7468                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
7469                 gtk_editable_set_position(GTK_EDITABLE(widget), 
7470                         strlen(str));
7471                 g_free(str);
7472                 if (widget->parent && widget->parent->parent
7473                  && widget->parent->parent->parent) {
7474                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
7475                                 gint y = widget->allocation.y;
7476                                 gint height = widget->allocation.height;
7477                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
7478                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
7479
7480                                 if (y < (int)shown->value) {
7481                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
7482                                 }
7483                                 if (y + height > (int)shown->value + (int)shown->page_size) {
7484                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
7485                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
7486                                                         y + height - (int)shown->page_size - 1);
7487                                         } else {
7488                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
7489                                                         (int)shown->upper - (int)shown->page_size - 1);
7490                                         }
7491                                 }
7492                         }
7493                 }
7494         }
7495
7496         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
7497                 compose->focused_editable = widget;
7498 }
7499
7500 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
7501 {
7502         compose->modified = TRUE;
7503         compose_set_title(compose);
7504 }
7505
7506 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
7507 {
7508         Compose *compose = (Compose *)data;
7509
7510         if (action == 1)
7511                 compose_wrap_all_full(compose, TRUE);
7512         else
7513                 compose_beautify_paragraph(compose, NULL, TRUE);
7514 }
7515
7516 static void compose_toggle_autowrap_cb(gpointer data, guint action,
7517                                        GtkWidget *widget)
7518 {
7519         Compose *compose = (Compose *)data;
7520         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
7521         if (compose->autowrap)
7522                 compose_wrap_all_full(compose, TRUE);
7523 }
7524
7525 static void compose_toggle_sign_cb(gpointer data, guint action,
7526                                    GtkWidget *widget)
7527 {
7528         Compose *compose = (Compose *)data;
7529
7530         if (GTK_CHECK_MENU_ITEM(widget)->active)
7531                 compose->use_signing = TRUE;
7532         else
7533                 compose->use_signing = FALSE;
7534 }
7535
7536 static void compose_toggle_encrypt_cb(gpointer data, guint action,
7537                                       GtkWidget *widget)
7538 {
7539         Compose *compose = (Compose *)data;
7540
7541         if (GTK_CHECK_MENU_ITEM(widget)->active)
7542                 compose->use_encryption = TRUE;
7543         else
7544                 compose->use_encryption = FALSE;
7545 }
7546
7547 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
7548 {
7549         g_free(compose->privacy_system);
7550         compose->privacy_system = g_strdup(account->default_privacy_system);
7551         compose_update_privacy_system_menu_item(compose, warn);
7552 }
7553
7554 static void compose_toggle_ruler_cb(gpointer data, guint action,
7555                                     GtkWidget *widget)
7556 {
7557         Compose *compose = (Compose *)data;
7558
7559         if (GTK_CHECK_MENU_ITEM(widget)->active) {
7560                 gtk_widget_show(compose->ruler_hbox);
7561                 prefs_common.show_ruler = TRUE;
7562         } else {
7563                 gtk_widget_hide(compose->ruler_hbox);
7564                 gtk_widget_queue_resize(compose->edit_vbox);
7565                 prefs_common.show_ruler = FALSE;
7566         }
7567 }
7568
7569 static void compose_attach_drag_received_cb (GtkWidget          *widget,
7570                                              GdkDragContext     *drag_context,
7571                                              gint                x,
7572                                              gint                y,
7573                                              GtkSelectionData   *data,
7574                                              guint               info,
7575                                              guint               time,
7576                                              gpointer            user_data)
7577 {
7578         Compose *compose = (Compose *)user_data;
7579         GList *list, *tmp;
7580
7581         if (gdk_atom_name(data->type) && 
7582             !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
7583                 list = uri_list_extract_filenames((const gchar *)data->data);
7584                 for (tmp = list; tmp != NULL; tmp = tmp->next)
7585                         compose_attach_append
7586                                 (compose, (const gchar *)tmp->data,
7587                                  (const gchar *)tmp->data, NULL);
7588                 if (list) compose_changed_cb(NULL, compose);
7589                 list_free_strings(list);
7590                 g_list_free(list);
7591         } else if (gdk_atom_name(data->type) && 
7592                    !strcmp(gdk_atom_name(data->type), "text/plain") &&
7593                    data->data && !strcmp(data->data, "Dummy-Summaryview")) {
7594                 /* comes from our summaryview */
7595                 SummaryView * summaryview = NULL;
7596                 GSList * list = NULL, *cur = NULL;
7597                 
7598                 if (mainwindow_get_mainwindow())
7599                         summaryview = mainwindow_get_mainwindow()->summaryview;
7600                 
7601                 if (summaryview)
7602                         list = summary_get_selected_msg_list(summaryview);
7603                 
7604                 for (cur = list; cur; cur = cur->next) {
7605                         MsgInfo *msginfo = (MsgInfo *)cur->data;
7606                         gchar *file = NULL;
7607                         if (msginfo)
7608                                 file = procmsg_get_message_file_full(msginfo, 
7609                                         TRUE, TRUE);
7610                         if (file) {
7611                                 compose_attach_append(compose, (const gchar *)file, 
7612                                         (const gchar *)file, "message/rfc822");
7613                                 g_free(file);
7614                         }
7615                 }
7616                 g_slist_free(list);
7617         }
7618 }
7619
7620 static gboolean compose_drag_drop(GtkWidget *widget,
7621                                   GdkDragContext *drag_context,
7622                                   gint x, gint y,
7623                                   guint time, gpointer user_data)
7624 {
7625         /* not handling this signal makes compose_insert_drag_received_cb
7626          * called twice */
7627         return TRUE;                                     
7628 }
7629
7630 static void compose_insert_drag_received_cb (GtkWidget          *widget,
7631                                              GdkDragContext     *drag_context,
7632                                              gint                x,
7633                                              gint                y,
7634                                              GtkSelectionData   *data,
7635                                              guint               info,
7636                                              guint               time,
7637                                              gpointer            user_data)
7638 {
7639         Compose *compose = (Compose *)user_data;
7640         GList *list, *tmp;
7641
7642         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
7643          * does not work */
7644         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
7645                 list = uri_list_extract_filenames((const gchar *)data->data);
7646                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
7647                                 compose_insert_file(compose, (const gchar *)tmp->data);
7648                 }
7649                 list_free_strings(list);
7650                 g_list_free(list);
7651                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
7652                 return;
7653         } else {
7654                 gchar *tmpfile = get_tmp_file();
7655                 str_write_to_file((const gchar *)data->data, tmpfile);
7656                 compose_insert_file(compose, tmpfile);
7657                 g_unlink(tmpfile);
7658                 g_free(tmpfile);
7659                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
7660                 return;
7661         }
7662         gtk_drag_finish(drag_context, TRUE, FALSE, time);
7663 }
7664
7665 static void compose_header_drag_received_cb (GtkWidget          *widget,
7666                                              GdkDragContext     *drag_context,
7667                                              gint                x,
7668                                              gint                y,
7669                                              GtkSelectionData   *data,
7670                                              guint               info,
7671                                              guint               time,
7672                                              gpointer            user_data)
7673 {
7674         GtkEditable *entry = (GtkEditable *)user_data;
7675         gchar *email = (gchar *)data->data;
7676
7677         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
7678          * does not work */
7679
7680         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
7681                 gchar *decoded=g_new(gchar, strlen(email));
7682                 int start = 0;
7683
7684                 email += strlen("mailto:");
7685                 decode_uri(decoded, email); /* will fit */
7686                 gtk_editable_delete_text(entry, 0, -1);
7687                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
7688                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
7689                 g_free(decoded);
7690                 return;
7691         }
7692         gtk_drag_finish(drag_context, TRUE, FALSE, time);
7693 }
7694
7695 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
7696                                              GtkWidget *widget)
7697 {
7698         Compose *compose = (Compose *)data;
7699
7700         if (GTK_CHECK_MENU_ITEM(widget)->active)
7701                 compose->return_receipt = TRUE;
7702         else
7703                 compose->return_receipt = FALSE;
7704 }
7705
7706 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
7707                                              GtkWidget *widget)
7708 {
7709         Compose *compose = (Compose *)data;
7710
7711         if (GTK_CHECK_MENU_ITEM(widget)->active)
7712                 compose->remove_references = TRUE;
7713         else
7714                 compose->remove_references = FALSE;
7715 }
7716
7717 gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
7718                                             GdkEventKey *event,
7719                                             ComposeHeaderEntry *headerentry)
7720 {
7721         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
7722             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
7723             !(event->state & GDK_MODIFIER_MASK) &&
7724             (event->keyval == GDK_BackSpace) &&
7725             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
7726                 gtk_container_remove
7727                         (GTK_CONTAINER(headerentry->compose->header_table),
7728                          headerentry->combo);
7729                 gtk_container_remove
7730                         (GTK_CONTAINER(headerentry->compose->header_table),
7731                          headerentry->entry);
7732                 headerentry->compose->header_list =
7733                         g_slist_remove(headerentry->compose->header_list,
7734                                        headerentry);
7735                 g_free(headerentry);
7736         } else  if (event->keyval == GDK_Tab) {
7737                 if (headerentry->compose->header_last == headerentry) {
7738                         /* Override default next focus, and give it to subject_entry
7739                          * instead of notebook tabs
7740                          */
7741                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
7742                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
7743                         return TRUE;
7744                 }
7745         }
7746         return FALSE;
7747 }
7748
7749 gboolean compose_headerentry_changed_cb(GtkWidget *entry,
7750                                     ComposeHeaderEntry *headerentry)
7751 {
7752         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
7753                 headerentry->compose->header_list =
7754                         g_slist_append(headerentry->compose->header_list,
7755                                        headerentry);
7756                 
7757                 compose_create_header_entry(headerentry->compose);
7758                 g_signal_handlers_disconnect_matched
7759                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
7760                          0, 0, NULL, NULL, headerentry);
7761                 
7762                 /* Automatically scroll down */
7763                 compose_show_first_last_header(headerentry->compose, FALSE);
7764                 
7765         }
7766         return FALSE;
7767 }
7768
7769 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
7770 {
7771         GtkAdjustment *vadj;
7772
7773         g_return_if_fail(compose);
7774         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
7775         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
7776
7777         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
7778         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
7779 }
7780
7781 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
7782                           const gchar *text, gint len, Compose *compose)
7783 {
7784         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
7785                                 (G_OBJECT(compose->text), "paste_as_quotation"));
7786         GtkTextMark *mark;
7787
7788         g_return_if_fail(text != NULL);
7789
7790         g_signal_handlers_block_by_func(G_OBJECT(buffer),
7791                                         G_CALLBACK(text_inserted),
7792                                         compose);
7793         if (paste_as_quotation) {
7794                 gchar *new_text;
7795                 gchar *qmark;
7796
7797                 if (len < 0)
7798                         len = strlen(text);
7799
7800                 new_text = g_strndup(text, len);
7801                 if (prefs_common.quotemark && *prefs_common.quotemark)
7802                         qmark = prefs_common.quotemark;
7803                 else
7804                         qmark = "> ";
7805
7806                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
7807                 gtk_text_buffer_place_cursor(buffer, iter);
7808
7809                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text);
7810                 g_free(new_text);
7811                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
7812                                   GINT_TO_POINTER(paste_as_quotation - 1));
7813                                   
7814                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
7815         } else
7816                 gtk_text_buffer_insert(buffer, iter, text, len);
7817
7818         mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
7819         
7820         compose_beautify_paragraph(compose, iter, FALSE);
7821
7822         gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
7823         gtk_text_buffer_delete_mark(buffer, mark);
7824
7825         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
7826                                           G_CALLBACK(text_inserted),
7827                                           compose);
7828         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
7829
7830         if (prefs_common.autosave && 
7831             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0)
7832                 compose->draft_timeout_tag = gtk_timeout_add
7833                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
7834 }
7835 static gint compose_defer_auto_save_draft(Compose *compose)
7836 {
7837         compose->draft_timeout_tag = -1;
7838         compose_draft_cb((gpointer)compose, COMPOSE_AUTO_SAVE, NULL);
7839         return FALSE;
7840 }
7841
7842 #if USE_ASPELL
7843 static void compose_check_all(Compose *compose)
7844 {
7845         if (compose->gtkaspell)
7846                 gtkaspell_check_all(compose->gtkaspell);
7847 }
7848
7849 static void compose_highlight_all(Compose *compose)
7850 {
7851         if (compose->gtkaspell)
7852                 gtkaspell_highlight_all(compose->gtkaspell);
7853 }
7854
7855 static void compose_check_backwards(Compose *compose)
7856 {
7857         if (compose->gtkaspell) 
7858                 gtkaspell_check_backwards(compose->gtkaspell);
7859         else {
7860                 GtkItemFactory *ifactory;
7861                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
7862                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
7863                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
7864         }
7865 }
7866
7867 static void compose_check_forwards_go(Compose *compose)
7868 {
7869         if (compose->gtkaspell) 
7870                 gtkaspell_check_forwards_go(compose->gtkaspell);
7871         else {
7872                 GtkItemFactory *ifactory;
7873                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
7874                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
7875                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
7876         }
7877 }
7878 #endif
7879
7880 /*!
7881  *\brief        Guess originating forward account from MsgInfo and several 
7882  *              "common preference" settings. Return NULL if no guess. 
7883  */
7884 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
7885 {
7886         PrefsAccount *account = NULL;
7887         
7888         g_return_val_if_fail(msginfo, NULL);
7889         g_return_val_if_fail(msginfo->folder, NULL);
7890         g_return_val_if_fail(msginfo->folder->prefs, NULL);
7891
7892         if (msginfo->folder->prefs->enable_default_account)
7893                 account = account_find_from_id(msginfo->folder->prefs->default_account);
7894                 
7895         if (!account) 
7896                 account = msginfo->folder->folder->account;
7897                 
7898         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
7899                 gchar *to;
7900                 Xstrdup_a(to, msginfo->to, return NULL);
7901                 extract_address(to);
7902                 account = account_find_from_address(to);
7903         }
7904
7905         if (!account && prefs_common.forward_account_autosel) {
7906                 gchar cc[BUFFSIZE];
7907                 if (!procheader_get_header_from_msginfo
7908                         (msginfo, cc,sizeof cc , "CC:")) { /* Found a CC header */
7909                         extract_address(cc);
7910                         account = account_find_from_address(cc);
7911                 }
7912         }
7913         
7914         return account;
7915 }
7916
7917 static gboolean compose_close(Compose *compose)
7918 {
7919         gint x, y;
7920
7921         if (!g_mutex_trylock(compose->mutex)) {
7922                 /* we have to wait for the (possibly deferred by auto-save)
7923                  * drafting to be done, before destroying the compose under
7924                  * it. */
7925                 debug_print("waiting for drafting to finish...\n");
7926                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
7927                 return FALSE;
7928         }
7929         g_return_val_if_fail(compose, FALSE);
7930         gtkut_widget_get_uposition(compose->window, &x, &y);
7931         prefs_common.compose_x = x;
7932         prefs_common.compose_y = y;
7933         g_mutex_unlock(compose->mutex);
7934         compose_destroy(compose);
7935         return FALSE;
7936 }
7937
7938 /**
7939  * Add entry field for each address in list.
7940  * \param compose     E-Mail composition object.
7941  * \param listAddress List of (formatted) E-Mail addresses.
7942  */
7943 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
7944         GList *node;
7945         gchar *addr;
7946         node = listAddress;
7947         while( node ) {
7948                 addr = ( gchar * ) node->data;
7949                 compose_entry_append( compose, addr, COMPOSE_TO );
7950                 node = g_list_next( node );
7951         }
7952 }
7953
7954 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
7955                                     guint action)
7956 {
7957         gchar *body;
7958         GSList *new_msglist = NULL;
7959         MsgInfo *tmp_msginfo = NULL;
7960         
7961         g_return_if_fail(msgview != NULL);
7962
7963         g_return_if_fail(msginfo_list != NULL);
7964
7965         if (g_slist_length(msginfo_list) == 1) {
7966                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
7967                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
7968                 
7969                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
7970                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
7971                         
7972                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
7973                                                 orig_msginfo, mimeinfo);
7974                         if (tmp_msginfo != NULL) {
7975                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
7976                         } 
7977                 }
7978         }
7979
7980         body = messageview_get_selection(msgview);
7981
7982         if (new_msglist) {
7983                 compose_reply_mode((ComposeMode)action, new_msglist, body);
7984                 procmsg_msginfo_free(tmp_msginfo);
7985                 g_slist_free(new_msglist);
7986         } else
7987                 compose_reply_mode((ComposeMode)action, msginfo_list, body);
7988
7989         g_free(body);
7990 }
7991
7992 /*
7993  * End of Source.
7994  */