2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2002 Hiroyuki Yamamoto
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtkmenuitem.h>
31 #include <gtk/gtkitemfactory.h>
32 #include <gtk/gtkcheckmenuitem.h>
33 #include <gtk/gtkoptionmenu.h>
34 #include <gtk/gtkwidget.h>
35 #include <gtk/gtkclist.h>
36 #include <gtk/gtkctree.h>
37 #include <gtk/gtkvpaned.h>
38 #include <gtk/gtkentry.h>
39 #include <gtk/gtkeditable.h>
40 #include <gtk/gtkwindow.h>
41 #include <gtk/gtksignal.h>
42 #include <gtk/gtkvbox.h>
43 #include <gtk/gtkcontainer.h>
44 #include <gtk/gtkhandlebox.h>
45 #include <gtk/gtktoolbar.h>
46 #include <gtk/gtktable.h>
47 #include <gtk/gtkhbox.h>
48 #include <gtk/gtklabel.h>
49 #include <gtk/gtkscrolledwindow.h>
50 #include <gtk/gtkthemes.h>
51 #include <gtk/gtkdnd.h>
56 #include <sys/types.h>
60 /* #include <sys/utsname.h> */
66 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
73 #include "mainwindow.h"
76 #include "addressbook.h"
77 #include "folderview.h"
80 #include "stock_pixmap.h"
84 #include "customheader.h"
85 #include "prefs_common.h"
86 #include "prefs_account.h"
87 #include "prefs_actions.h"
90 #include "procheader.h"
92 #include "statusbar.h"
99 #include "alertpanel.h"
100 #include "manage_window.h"
101 #include "gtkshruler.h"
103 #include "addr_compl.h"
104 #include "quote_fmt.h"
105 #include "template.h"
107 #include "foldersel.h"
111 # include "rfc2015.h"
121 #define N_ATTACH_COLS 3
125 COMPOSE_CALL_GTK_STEXT_MOVE_BEGINNING_OF_LINE
,
126 COMPOSE_CALL_GTK_STEXT_MOVE_FORWARD_CHARACTER
,
127 COMPOSE_CALL_GTK_STEXT_MOVE_BACKWARD_CHARACTER
,
128 COMPOSE_CALL_GTK_STEXT_MOVE_FORWARD_WORD
,
129 COMPOSE_CALL_GTK_STEXT_MOVE_BACKWARD_WORD
,
130 COMPOSE_CALL_GTK_STEXT_MOVE_END_OF_LINE
,
131 COMPOSE_CALL_GTK_STEXT_MOVE_NEXT_LINE
,
132 COMPOSE_CALL_GTK_STEXT_MOVE_PREVIOUS_LINE
,
133 COMPOSE_CALL_GTK_STEXT_DELETE_FORWARD_CHARACTER
,
134 COMPOSE_CALL_GTK_STEXT_DELETE_BACKWARD_CHARACTER
,
135 COMPOSE_CALL_GTK_STEXT_DELETE_FORWARD_WORD
,
136 COMPOSE_CALL_GTK_STEXT_DELETE_BACKWARD_WORD
,
137 COMPOSE_CALL_GTK_STEXT_DELETE_LINE
,
138 COMPOSE_CALL_GTK_STEXT_DELETE_LINE_N
,
139 COMPOSE_CALL_GTK_STEXT_DELETE_TO_LINE_END
140 } ComposeCallGtkSTextAction
;
144 PRIORITY_HIGHEST
= 1,
151 #define B64_LINE_SIZE 57
152 #define B64_BUFFSIZE 77
154 #define MAX_REFERENCES_LEN 999
156 static GdkColor quote_color
= {0, 0, 0, 0xbfff};
158 static GList
*compose_list
= NULL
;
160 Compose
*compose_generic_new (PrefsAccount
*account
,
163 GPtrArray
*attach_files
);
165 static Compose
*compose_create (PrefsAccount
*account
,
168 static GtkWidget
*compose_account_option_menu_create
170 static void compose_set_template_menu (Compose
*compose
);
171 static void compose_template_apply (Compose
*compose
,
174 static void compose_destroy (Compose
*compose
);
176 static void compose_entries_set (Compose
*compose
,
177 const gchar
*mailto
);
178 static gint
compose_parse_header (Compose
*compose
,
180 static gchar
*compose_parse_references (const gchar
*ref
,
183 static gchar
*compose_quote_fmt (Compose
*compose
,
189 static void compose_reply_set_entry (Compose
*compose
,
195 followup_and_reply_to
);
196 static void compose_reedit_set_entry (Compose
*compose
,
198 static void compose_insert_sig (Compose
*compose
);
199 static void compose_insert_file (Compose
*compose
,
201 static void compose_attach_append (Compose
*compose
,
204 const gchar
*content_type
);
205 static void compose_attach_parts (Compose
*compose
,
207 static void compose_wrap_line (Compose
*compose
);
208 static void compose_wrap_line_all (Compose
*compose
);
209 static void compose_wrap_line_all_full (Compose
*compose
,
211 static void compose_set_title (Compose
*compose
);
212 static void compose_select_account (Compose
*compose
,
213 PrefsAccount
*account
);
215 static PrefsAccount
*compose_current_mail_account(void);
216 /* static gint compose_send (Compose *compose); */
217 static gboolean compose_check_for_valid_recipient
219 static gboolean
compose_check_entries (Compose
*compose
,
220 gboolean check_subject
);
221 static gint
compose_write_to_file (Compose
*compose
,
224 static gint
compose_write_body_to_file (Compose
*compose
,
226 static gint
compose_remove_reedit_target (Compose
*compose
);
227 void compose_remove_draft (Compose
*compose
);
228 static gint
compose_queue (Compose
*compose
,
231 static gint
compose_queue_sub (Compose
*compose
,
234 gboolean check_subject
);
235 static void compose_write_attach (Compose
*compose
,
237 static gint
compose_write_headers (Compose
*compose
,
239 const gchar
*charset
,
240 EncodingType encoding
,
243 static void compose_convert_header (gchar
*dest
,
247 static void compose_generate_msgid (Compose
*compose
,
251 static void compose_attach_info_free (AttachInfo
*ainfo
);
252 static void compose_attach_remove_selected (Compose
*compose
);
254 static void compose_attach_property (Compose
*compose
);
255 static void compose_attach_property_create (gboolean
*cancelled
);
256 static void attach_property_ok (GtkWidget
*widget
,
257 gboolean
*cancelled
);
258 static void attach_property_cancel (GtkWidget
*widget
,
259 gboolean
*cancelled
);
260 static gint
attach_property_delete_event (GtkWidget
*widget
,
262 gboolean
*cancelled
);
263 static void attach_property_key_pressed (GtkWidget
*widget
,
265 gboolean
*cancelled
);
267 static void compose_exec_ext_editor (Compose
*compose
);
268 static gint
compose_exec_ext_editor_real (const gchar
*file
);
269 static gboolean
compose_ext_editor_kill (Compose
*compose
);
270 static void compose_input_cb (gpointer data
,
272 GdkInputCondition condition
);
273 static void compose_set_ext_editor_sensitive (Compose
*compose
,
276 static void compose_undo_state_changed (UndoMain
*undostruct
,
281 static gint
calc_cursor_xpos (GtkSText
*text
,
285 static void compose_create_header_entry (Compose
*compose
);
286 static void compose_add_header_entry (Compose
*compose
, gchar
*header
, gchar
*text
);
287 static void compose_update_priority_menu_item(Compose
* compose
);
289 /* callback functions */
291 static gboolean
compose_edit_size_alloc (GtkEditable
*widget
,
292 GtkAllocation
*allocation
,
293 GtkSHRuler
*shruler
);
294 static void account_activated (GtkMenuItem
*menuitem
,
296 static void attach_selected (GtkCList
*clist
,
301 static void attach_button_pressed (GtkWidget
*widget
,
302 GdkEventButton
*event
,
304 static void attach_key_pressed (GtkWidget
*widget
,
308 static void compose_send_cb (gpointer data
,
311 static void compose_send_later_cb (gpointer data
,
315 static void compose_draft_cb (gpointer data
,
319 static void compose_attach_cb (gpointer data
,
322 static void compose_insert_file_cb (gpointer data
,
326 static void compose_close_cb (gpointer data
,
330 static void compose_address_cb (gpointer data
,
333 static void compose_template_activate_cb(GtkWidget
*widget
,
336 static void compose_ext_editor_cb (gpointer data
,
340 static gint
compose_delete_cb (GtkWidget
*widget
,
343 static void compose_destroy_cb (GtkWidget
*widget
,
346 static void compose_undo_cb (Compose
*compose
);
347 static void compose_redo_cb (Compose
*compose
);
348 static void compose_cut_cb (Compose
*compose
);
349 static void compose_copy_cb (Compose
*compose
);
350 static void compose_paste_cb (Compose
*compose
);
351 static void compose_paste_as_quote_cb (Compose
*compose
);
352 static void compose_allsel_cb (Compose
*compose
);
354 static void compose_gtk_stext_action_cb (Compose
*compose
,
355 ComposeCallGtkSTextAction action
);
357 static void compose_grab_focus_cb (GtkWidget
*widget
,
360 static void compose_changed_cb (GtkEditable
*editable
,
362 static void compose_button_press_cb (GtkWidget
*widget
,
363 GdkEventButton
*event
,
366 static void compose_key_press_cb (GtkWidget
*widget
,
372 static void compose_toggle_to_cb (gpointer data
,
375 static void compose_toggle_cc_cb (gpointer data
,
378 static void compose_toggle_bcc_cb (gpointer data
,
381 static void compose_toggle_replyto_cb (gpointer data
,
384 static void compose_toggle_followupto_cb(gpointer data
,
387 static void compose_toggle_attach_cb (gpointer data
,
391 static void compose_toggle_ruler_cb (gpointer data
,
395 static void compose_toggle_sign_cb (gpointer data
,
398 static void compose_toggle_encrypt_cb (gpointer data
,
401 static void compose_set_gnupg_mode_cb (gpointer data
,
404 static void compose_update_gnupg_mode_menu_item(Compose
* compose
);
405 static void activate_gnupg_mode (Compose
*compose
,
406 PrefsAccount
*account
);
408 static void compose_toggle_return_receipt_cb(gpointer data
, guint action
,
410 static void compose_set_priority_cb (gpointer data
,
414 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
415 GdkDragContext
*drag_context
,
418 GtkSelectionData
*data
,
422 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
423 GdkDragContext
*drag_context
,
426 GtkSelectionData
*data
,
432 static void to_activated (GtkWidget
*widget
,
434 static void newsgroups_activated (GtkWidget
*widget
,
436 static void cc_activated (GtkWidget
*widget
,
438 static void bcc_activated (GtkWidget
*widget
,
440 static void replyto_activated (GtkWidget
*widget
,
442 static void followupto_activated (GtkWidget
*widget
,
444 static void subject_activated (GtkWidget
*widget
,
448 static void text_activated (GtkWidget
*widget
,
450 static void text_inserted (GtkWidget
*widget
,
455 static void compose_generic_reply(MsgInfo
*msginfo
, gboolean quote
,
456 gboolean to_all
, gboolean to_ml
,
457 gboolean ignore_replyto
,
458 gboolean followup_and_reply_to
,
461 void compose_headerentry_changed_cb (GtkWidget
*entry
,
462 ComposeHeaderEntry
*headerentry
);
463 void compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
465 ComposeHeaderEntry
*headerentry
);
467 static void compose_show_first_last_header (Compose
*compose
, gboolean show_first
);
470 static void compose_check_all (Compose
*compose
);
471 static void compose_highlight_all (Compose
*compose
);
472 static void compose_check_backwards (Compose
*compose
);
473 static void compose_check_forwards_go (Compose
*compose
);
476 static gboolean
compose_send_control_enter (Compose
*compose
);
478 static GtkItemFactoryEntry compose_popup_entries
[] =
480 {N_("/_Add..."), NULL
, compose_attach_cb
, 0, NULL
},
481 {N_("/_Remove"), NULL
, compose_attach_remove_selected
, 0, NULL
},
482 {N_("/---"), NULL
, NULL
, 0, "<Separator>"},
483 {N_("/_Property..."), NULL
, compose_attach_property
, 0, NULL
}
486 static GtkItemFactoryEntry compose_entries
[] =
488 {N_("/_File"), NULL
, NULL
, 0, "<Branch>"},
489 {N_("/_File/_Attach file"), "<control>M", compose_attach_cb
, 0, NULL
},
490 {N_("/_File/_Insert file"), "<control>I", compose_insert_file_cb
, 0, NULL
},
491 {N_("/_File/Insert si_gnature"), "<control>G", compose_insert_sig
, 0, NULL
},
492 {N_("/_File/---"), NULL
, NULL
, 0, "<Separator>"},
493 {N_("/_File/_Close"), "<control>W", compose_close_cb
, 0, NULL
},
495 {N_("/_Edit"), NULL
, NULL
, 0, "<Branch>"},
496 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb
, 0, NULL
},
497 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb
, 0, NULL
},
498 {N_("/_Edit/---"), NULL
, NULL
, 0, "<Separator>"},
499 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb
, 0, NULL
},
500 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb
, 0, NULL
},
501 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb
, 0, NULL
},
502 {N_("/_Edit/Paste as _quotation"),
503 NULL
, compose_paste_as_quote_cb
, 0, NULL
},
504 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb
, 0, NULL
},
505 {N_("/_Edit/A_dvanced"), NULL
, NULL
, 0, "<Branch>"},
506 {N_("/_Edit/A_dvanced/Move a character backward"),
508 compose_gtk_stext_action_cb
,
509 COMPOSE_CALL_GTK_STEXT_MOVE_BACKWARD_CHARACTER
,
511 {N_("/_Edit/A_dvanced/Move a character forward"),
513 compose_gtk_stext_action_cb
,
514 COMPOSE_CALL_GTK_STEXT_MOVE_FORWARD_CHARACTER
,
516 {N_("/_Edit/A_dvanced/Move a word backward"),
518 compose_gtk_stext_action_cb
,
519 COMPOSE_CALL_GTK_STEXT_MOVE_BACKWARD_WORD
,
521 {N_("/_Edit/A_dvanced/Move a word forward"),
523 compose_gtk_stext_action_cb
,
524 COMPOSE_CALL_GTK_STEXT_MOVE_FORWARD_WORD
,
526 {N_("/_Edit/A_dvanced/Move to beginning of line"),
527 NULL
, /* "<control>A" */
528 compose_gtk_stext_action_cb
,
529 COMPOSE_CALL_GTK_STEXT_MOVE_BEGINNING_OF_LINE
,
531 {N_("/_Edit/A_dvanced/Move to end of line"),
533 compose_gtk_stext_action_cb
,
534 COMPOSE_CALL_GTK_STEXT_MOVE_END_OF_LINE
,
536 {N_("/_Edit/A_dvanced/Move to previous line"),
538 compose_gtk_stext_action_cb
,
539 COMPOSE_CALL_GTK_STEXT_MOVE_PREVIOUS_LINE
,
541 {N_("/_Edit/A_dvanced/Move to next line"),
543 compose_gtk_stext_action_cb
,
544 COMPOSE_CALL_GTK_STEXT_MOVE_NEXT_LINE
,
546 {N_("/_Edit/A_dvanced/Delete a character backward"),
548 compose_gtk_stext_action_cb
,
549 COMPOSE_CALL_GTK_STEXT_DELETE_BACKWARD_CHARACTER
,
551 {N_("/_Edit/A_dvanced/Delete a character forward"),
553 compose_gtk_stext_action_cb
,
554 COMPOSE_CALL_GTK_STEXT_DELETE_FORWARD_CHARACTER
,
556 {N_("/_Edit/A_dvanced/Delete a word backward"),
557 NULL
, /* "<control>W" */
558 compose_gtk_stext_action_cb
,
559 COMPOSE_CALL_GTK_STEXT_DELETE_BACKWARD_WORD
,
561 {N_("/_Edit/A_dvanced/Delete a word forward"),
562 NULL
, /* "<alt>D", */
563 compose_gtk_stext_action_cb
,
564 COMPOSE_CALL_GTK_STEXT_DELETE_FORWARD_WORD
,
566 {N_("/_Edit/A_dvanced/Delete line"),
568 compose_gtk_stext_action_cb
,
569 COMPOSE_CALL_GTK_STEXT_DELETE_LINE
,
571 {N_("/_Edit/A_dvanced/Delete entire line"),
573 compose_gtk_stext_action_cb
,
574 COMPOSE_CALL_GTK_STEXT_DELETE_LINE_N
,
576 {N_("/_Edit/A_dvanced/Delete to end of line"),
578 compose_gtk_stext_action_cb
,
579 COMPOSE_CALL_GTK_STEXT_DELETE_TO_LINE_END
,
581 {N_("/_Edit/---"), NULL
, NULL
, 0, "<Separator>"},
582 {N_("/_Edit/_Wrap current paragraph"),
583 "<control>L", compose_wrap_line
, 0, NULL
},
584 {N_("/_Edit/Wrap all long _lines"),
585 "<control><alt>L", compose_wrap_line_all
, 0, NULL
},
586 {N_("/_Edit/Edit with e_xternal editor"),
587 "<shift><control>X", compose_ext_editor_cb
, 0, NULL
},
589 {N_("/_Spelling"), NULL
, NULL
, 0, "<Branch>"},
590 {N_("/_Spelling/_Check all or check selection"),
591 NULL
, compose_check_all
, 0, NULL
},
592 {N_("/_Spelling/_Highlight all misspelled words"),
593 NULL
, compose_highlight_all
, 0, NULL
},
594 {N_("/_Spelling/Check _backwards misspelled word"),
595 NULL
, compose_check_backwards
, 0, NULL
},
596 {N_("/_Spelling/_Forward to next misspelled word"),
597 NULL
, compose_check_forwards_go
, 0, NULL
},
598 {N_("/_Spelling/---"), NULL
, NULL
, 0, "<Separator>"},
599 {N_("/_Spelling/_Spelling Configuration"),
600 NULL
, NULL
, 0, "<Branch>"},
602 #if 0 /* NEW COMPOSE GUI */
603 {N_("/_View"), NULL
, NULL
, 0, "<Branch>"},
604 {N_("/_View/_To"), NULL
, compose_toggle_to_cb
, 0, "<ToggleItem>"},
605 {N_("/_View/_Cc"), NULL
, compose_toggle_cc_cb
, 0, "<ToggleItem>"},
606 {N_("/_View/_Bcc"), NULL
, compose_toggle_bcc_cb
, 0, "<ToggleItem>"},
607 {N_("/_View/_Reply to"), NULL
, compose_toggle_replyto_cb
, 0, "<ToggleItem>"},
608 {N_("/_View/---"), NULL
, NULL
, 0, "<Separator>"},
609 {N_("/_View/_Followup to"), NULL
, compose_toggle_followupto_cb
, 0, "<ToggleItem>"},
610 {N_("/_View/---"), NULL
, NULL
, 0, "<Separator>"},
611 {N_("/_View/R_uler"), NULL
, compose_toggle_ruler_cb
, 0, "<ToggleItem>"},
612 {N_("/_View/---"), NULL
, NULL
, 0, "<Separator>"},
613 {N_("/_View/_Attachment"), NULL
, compose_toggle_attach_cb
, 0, "<ToggleItem>"},
615 {N_("/_Message"), NULL
, NULL
, 0, "<Branch>"},
616 {N_("/_Message/_Send"), "<control>Return",
617 compose_send_cb
, 0, NULL
},
618 {N_("/_Message/Send _later"), "<shift><control>S",
619 compose_send_later_cb
, 0, NULL
},
620 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
621 {N_("/_Message/Save to _draft folder"),
622 "<shift><control>D", compose_draft_cb
, 0, NULL
},
623 {N_("/_Message/Save and _keep editing"),
624 "<control>S", compose_draft_cb
, 1, NULL
},
625 #if 0 /* NEW COMPOSE GUI */
626 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
627 {N_("/_Message/_To"), NULL
, compose_toggle_to_cb
, 0, "<ToggleItem>"},
628 {N_("/_Message/_Cc"), NULL
, compose_toggle_cc_cb
, 0, "<ToggleItem>"},
629 {N_("/_Message/_Bcc"), NULL
, compose_toggle_bcc_cb
, 0, "<ToggleItem>"},
630 {N_("/_Message/_Reply to"), NULL
, compose_toggle_replyto_cb
, 0, "<ToggleItem>"},
631 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
632 {N_("/_Message/_Followup to"), NULL
, compose_toggle_followupto_cb
, 0, "<ToggleItem>"},
633 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
634 {N_("/_Message/_Attach"), NULL
, compose_toggle_attach_cb
, 0, "<ToggleItem>"},
637 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
638 {N_("/_Message/Si_gn"), NULL
, compose_toggle_sign_cb
, 0, "<ToggleItem>"},
639 {N_("/_Message/_Encrypt"), NULL
, compose_toggle_encrypt_cb
, 0, "<ToggleItem>"},
640 {N_("/_Message/Mode/MIME"), NULL
, compose_set_gnupg_mode_cb
, GNUPG_MODE_DETACH
, "<RadioItem>"},
641 {N_("/_Message/Mode/Inline"), NULL
, compose_set_gnupg_mode_cb
, GNUPG_MODE_INLINE
, "/Message/Mode/MIME"},
642 #endif /* USE_GPGME */
643 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
644 {N_("/_Message/_Priority"), NULL
, NULL
, 0, "<Branch>"},
645 {N_("/_Message/Priority/_Highest"), NULL
, compose_set_priority_cb
, PRIORITY_HIGHEST
, "<RadioItem>"},
646 {N_("/_Message/Priority/Hi_gh"), NULL
, compose_set_priority_cb
, PRIORITY_HIGH
, "/Message/Priority/Highest"},
647 {N_("/_Message/Priority/_Normal"), NULL
, compose_set_priority_cb
, PRIORITY_NORMAL
, "/Message/Priority/Highest"},
648 {N_("/_Message/Priority/Lo_w"), NULL
, compose_set_priority_cb
, PRIORITY_LOW
, "/Message/Priority/Highest"},
649 {N_("/_Message/Priority/_Lowest"), NULL
, compose_set_priority_cb
, PRIORITY_LOWEST
, "/Message/Priority/Highest"},
650 {N_("/_Message/---"), NULL
, NULL
, 0, "<Separator>"},
651 {N_("/_Message/_Request Return Receipt"), NULL
, compose_toggle_return_receipt_cb
, 0, "<ToggleItem>"},
652 {N_("/_Tools"), NULL
, NULL
, 0, "<Branch>"},
653 {N_("/_Tools/Show _ruler"), NULL
, compose_toggle_ruler_cb
, 0, "<ToggleItem>"},
654 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb
, 0, NULL
},
655 {N_("/_Tools/_Template"), NULL
, NULL
, 0, "<Branch>"},
656 {N_("/_Tools/Actio_ns"), NULL
, NULL
, 0, "<Branch>"},
657 {N_("/_Help"), NULL
, NULL
, 0, "<Branch>"},
658 {N_("/_Help/_About"), NULL
, about_show
, 0, NULL
}
661 static GtkTargetEntry compose_mime_types
[] =
663 {"text/uri-list", 0, 0}
666 Compose
*compose_new(PrefsAccount
*account
, const gchar
*mailto
,
667 GPtrArray
*attach_files
)
669 return compose_generic_new(account
, mailto
, NULL
, attach_files
);
672 Compose
*compose_new_with_folderitem(PrefsAccount
*account
, FolderItem
*item
)
674 return compose_generic_new(account
, NULL
, item
, NULL
);
677 Compose
*compose_generic_new(PrefsAccount
*account
, const gchar
*mailto
, FolderItem
*item
,
678 GPtrArray
*attach_files
)
682 GtkItemFactory
*ifactory
;
683 gboolean grab_focus_on_last
= TRUE
;
685 if (item
&& item
->prefs
&& item
->prefs
->enable_default_account
)
686 account
= account_find_from_id(item
->prefs
->default_account
);
688 if (!account
) account
= cur_account
;
689 g_return_val_if_fail(account
!= NULL
, NULL
);
691 compose
= compose_create(account
, COMPOSE_NEW
);
692 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
694 compose
->replyinfo
= NULL
;
696 text
= GTK_STEXT(compose
->text
);
697 gtk_stext_freeze(text
);
699 if (prefs_common
.auto_sig
)
700 compose_insert_sig(compose
);
701 gtk_editable_set_position(GTK_EDITABLE(text
), 0);
702 gtk_stext_set_point(text
, 0);
704 gtk_stext_thaw(text
);
706 if (account
->protocol
!= A_NNTP
) {
707 if (mailto
&& *mailto
!= '\0') {
708 compose_entries_set(compose
, mailto
);
710 } else if (item
&& item
->prefs
->enable_default_to
) {
711 compose_entry_append(compose
, item
->prefs
->default_to
, COMPOSE_TO
);
712 compose_entry_select(compose
, item
->prefs
->default_to
);
713 grab_focus_on_last
= FALSE
;
715 if (item
&& item
->ret_rcpt
) {
716 menu_set_toggle(ifactory
, "/Message/Request Return Receipt", TRUE
);
720 compose_entry_append(compose
, mailto
, COMPOSE_NEWSGROUPS
);
723 * CLAWS: just don't allow return receipt request, even if the user
724 * may want to send an email. simple but foolproof.
726 menu_set_sensitive(ifactory
, "/Message/Request Return Receipt", FALSE
);
733 for (i
= 0; i
< attach_files
->len
; i
++) {
734 file
= g_ptr_array_index(attach_files
, i
);
735 compose_attach_append(compose
, file
, file
, NULL
);
739 compose_show_first_last_header(compose
, TRUE
);
741 /* Set save folder */
742 if (item
&& item
->prefs
&& item
->prefs
->save_copy_to_folder
) {
743 gchar
*folderidentifier
;
745 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), prefs_common
.savemsg
);
746 folderidentifier
= folder_item_get_identifier(item
);
747 gtk_entry_set_text(GTK_ENTRY(compose
->savemsg_entry
), folderidentifier
);
748 g_free(folderidentifier
);
751 /* Grab focus on last header only if no default_to was set */
752 if (grab_focus_on_last
)
753 gtk_widget_grab_focus(compose
->header_last
->entry
);
755 if (prefs_common
.auto_exteditor
)
756 compose_exec_ext_editor(compose
);
762 Compose *compose_new_followup_and_replyto(PrefsAccount *account,
763 const gchar *followupto, gchar * to)
767 if (!account) account = cur_account;
768 g_return_val_if_fail(account != NULL, NULL);
769 g_return_val_if_fail(account->protocol != A_NNTP, NULL);
771 compose = compose_create(account, COMPOSE_NEW);
773 if (prefs_common.auto_sig)
774 compose_insert_sig(compose);
775 gtk_editable_set_position(GTK_EDITABLE(compose->text), 0);
776 gtk_stext_set_point(GTK_STEXT(compose->text), 0);
778 compose_entry_append(compose, to, COMPOSE_TO);
779 compose_entry_append(compose, followupto, COMPOSE_NEWSGROUPS);
780 gtk_widget_grab_focus(compose->subject_entry);
786 void compose_reply(MsgInfo
*msginfo
, gboolean quote
, gboolean to_all
,
787 gboolean to_ml
, gboolean ignore_replyto
,
790 compose_generic_reply(msginfo
, quote
, to_all
, to_ml
,
791 ignore_replyto
, FALSE
, body
);
794 void compose_followup_and_reply_to(MsgInfo
*msginfo
, gboolean quote
,
796 gboolean ignore_replyto
,
799 compose_generic_reply(msginfo
, quote
, to_all
, FALSE
,
800 ignore_replyto
, TRUE
, body
);
803 static void compose_generic_reply(MsgInfo
*msginfo
, gboolean quote
,
804 gboolean to_all
, gboolean to_ml
,
805 gboolean ignore_replyto
,
806 gboolean followup_and_reply_to
,
810 PrefsAccount
*account
;
811 PrefsAccount
*reply_account
;
814 g_return_if_fail(msginfo
!= NULL
);
815 g_return_if_fail(msginfo
->folder
!= NULL
);
818 /* select the account set in folderitem's property (if enabled) */
819 if (msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->enable_default_account
)
820 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
822 /* select the account for the whole folder (IMAP / NNTP) */
824 /* FIXME: this is not right, because folder may be nested. we should
825 * ascend the tree until we find a parent with proper account
827 account
= msginfo
->folder
->folder
->account
;
829 /* select account by to: and cc: header if enabled */
830 if (prefs_common
.reply_account_autosel
) {
831 if (!account
&& msginfo
->to
) {
833 Xstrdup_a(to
, msginfo
->to
, return);
835 account
= account_find_from_address(to
);
839 if (!get_header_from_msginfo(msginfo
, cc
, sizeof(cc
), "CC:")) { /* Found a CC header */
841 account
= account_find_from_address(cc
);
846 /* select current account */
847 if (!account
) account
= cur_account
;
848 g_return_if_fail(account
!= NULL
);
850 if (ignore_replyto
&& account
->protocol
== A_NNTP
&&
851 !followup_and_reply_to
) {
853 account_find_from_address(account
->address
);
855 reply_account
= compose_current_mail_account();
859 reply_account
= account
;
861 compose
= compose_create(account
, COMPOSE_REPLY
);
863 compose
->replyinfo
= procmsg_msginfo_get_full_info(msginfo
);
864 if (!compose
->replyinfo
)
865 compose
->replyinfo
= procmsg_msginfo_copy(msginfo
);
867 if (msginfo
->folder
&& msginfo
->folder
->ret_rcpt
) {
868 GtkItemFactory
*ifactory
;
870 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
871 menu_set_toggle(ifactory
, "/Message/Request Return Receipt", TRUE
);
874 /* Set save folder */
875 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
876 gchar
*folderidentifier
;
878 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
879 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
880 gtk_entry_set_text(GTK_ENTRY(compose
->savemsg_entry
), folderidentifier
);
881 g_free(folderidentifier
);
884 if (compose_parse_header(compose
, msginfo
) < 0) return;
885 compose_reply_set_entry(compose
, msginfo
, to_all
, to_ml
,
886 ignore_replyto
, followup_and_reply_to
);
887 compose_show_first_last_header(compose
, TRUE
);
889 text
= GTK_STEXT(compose
->text
);
890 gtk_stext_freeze(text
);
896 if (prefs_common
.quotemark
&& *prefs_common
.quotemark
)
897 qmark
= prefs_common
.quotemark
;
901 quote_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
902 prefs_common
.quotefmt
,
906 if (prefs_common
.auto_sig
)
907 compose_insert_sig(compose
);
909 if (quote
&& prefs_common
.linewrap_quote
)
910 compose_wrap_line_all(compose
);
912 gtk_editable_set_position(GTK_EDITABLE(text
), 0);
913 gtk_stext_set_point(text
, 0);
915 gtk_stext_thaw(text
);
916 gtk_widget_grab_focus(compose
->text
);
918 if (prefs_common
.auto_exteditor
)
919 compose_exec_ext_editor(compose
);
922 #define INSERT_FW_HEADER(var, hdr) \
923 if (msginfo->var && *msginfo->var) { \
924 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
925 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
926 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
929 Compose
*compose_forward(PrefsAccount
*account
, MsgInfo
*msginfo
,
930 gboolean as_attach
, const gchar
*body
)
935 g_return_val_if_fail(msginfo
!= NULL
, NULL
);
936 g_return_val_if_fail(msginfo
->folder
!= NULL
, NULL
);
938 if (msginfo
->folder
->prefs
->enable_default_account
)
939 account
= account_find_from_id(msginfo
->folder
->prefs
->default_account
);
941 account
= msginfo
->folder
->folder
->account
;
942 if (!account
&& msginfo
->to
&& prefs_common
.forward_account_autosel
) {
944 Xstrdup_a(to
, msginfo
->to
, return NULL
);
946 account
= account_find_from_address(to
);
949 if (!account
&& prefs_common
.forward_account_autosel
) {
951 if (!get_header_from_msginfo(msginfo
,cc
,sizeof(cc
),"CC:")){ /* Found a CC header */
953 account
= account_find_from_address(cc
);
957 if (account
== NULL
) {
958 account
= cur_account
;
960 account = msginfo->folder->folder->account;
961 if (!account) account = cur_account;
964 g_return_val_if_fail(account
!= NULL
, NULL
);
966 MSG_UNSET_PERM_FLAGS(msginfo
->flags
, MSG_REPLIED
);
967 MSG_SET_PERM_FLAGS(msginfo
->flags
, MSG_FORWARDED
);
968 if (MSG_IS_IMAP(msginfo
->flags
))
969 imap_msg_unset_perm_flags(msginfo
, MSG_REPLIED
);
971 compose
= compose_create(account
, COMPOSE_FORWARD
);
973 if (msginfo
->subject
&& *msginfo
->subject
) {
974 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Fw: ");
975 gtk_entry_append_text(GTK_ENTRY(compose
->subject_entry
),
979 text
= GTK_STEXT(compose
->text
);
980 gtk_stext_freeze(text
);
986 msgfile
= procmsg_get_message_file_path(msginfo
);
987 if (!is_file_exist(msgfile
))
988 g_warning("%s: file not exist\n", msgfile
);
990 compose_attach_append(compose
, msgfile
, msgfile
,
997 MsgInfo
*full_msginfo
;
999 full_msginfo
= procmsg_msginfo_get_full_info(msginfo
);
1001 full_msginfo
= procmsg_msginfo_copy(msginfo
);
1003 if (prefs_common
.fw_quotemark
&&
1004 *prefs_common
.fw_quotemark
)
1005 qmark
= prefs_common
.fw_quotemark
;
1009 quote_str
= compose_quote_fmt(compose
, full_msginfo
,
1010 prefs_common
.fw_quotefmt
,
1012 compose_attach_parts(compose
, msginfo
);
1014 procmsg_msginfo_free(full_msginfo
);
1017 if (prefs_common
.auto_sig
)
1018 compose_insert_sig(compose
);
1020 if (prefs_common
.linewrap_quote
)
1021 compose_wrap_line_all(compose
);
1023 gtk_editable_set_position(GTK_EDITABLE(compose
->text
), 0);
1024 gtk_stext_set_point(GTK_STEXT(compose
->text
), 0);
1026 gtk_stext_thaw(text
);
1027 #if 0 /* NEW COMPOSE GUI */
1028 if (account
->protocol
!= A_NNTP
)
1029 gtk_widget_grab_focus(compose
->to_entry
);
1031 gtk_widget_grab_focus(compose
->newsgroups_entry
);
1033 gtk_widget_grab_focus(compose
->header_last
->entry
);
1035 if (prefs_common
.auto_exteditor
)
1036 compose_exec_ext_editor(compose
);
1039 if (msginfo
->folder
&& msginfo
->folder
->prefs
&& msginfo
->folder
->prefs
->save_copy_to_folder
) {
1040 gchar
*folderidentifier
;
1042 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1043 folderidentifier
= folder_item_get_identifier(msginfo
->folder
);
1044 gtk_entry_set_text(GTK_ENTRY(compose
->savemsg_entry
), folderidentifier
);
1045 g_free(folderidentifier
);
1051 #undef INSERT_FW_HEADER
1053 Compose
*compose_forward_multiple(PrefsAccount
*account
, GSList
*msginfo_list
)
1060 g_return_val_if_fail(msginfo_list
!= NULL
, NULL
);
1062 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1063 if ( ((MsgInfo
*)msginfo
->data
)->folder
== NULL
)
1067 if (account
== NULL
) {
1068 account
= cur_account
;
1070 account = msginfo->folder->folder->account;
1071 if (!account) account = cur_account;
1074 g_return_val_if_fail(account
!= NULL
, NULL
);
1076 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1077 MSG_UNSET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_REPLIED
);
1078 MSG_SET_PERM_FLAGS(((MsgInfo
*)msginfo
->data
)->flags
, MSG_FORWARDED
);
1081 compose
= compose_create(account
, COMPOSE_FORWARD
);
1083 text
= GTK_STEXT(compose
->text
);
1084 gtk_stext_freeze(text
);
1086 for (msginfo
= msginfo_list
; msginfo
!= NULL
; msginfo
= msginfo
->next
) {
1087 msgfile
= procmsg_get_message_file_path((MsgInfo
*)msginfo
->data
);
1088 if (!is_file_exist(msgfile
))
1089 g_warning("%s: file not exist\n", msgfile
);
1091 compose_attach_append(compose
, msgfile
, msgfile
,
1096 if (prefs_common
.auto_sig
)
1097 compose_insert_sig(compose
);
1099 if (prefs_common
.linewrap_quote
)
1100 compose_wrap_line_all(compose
);
1102 gtk_editable_set_position(GTK_EDITABLE(compose
->text
), 0);
1103 gtk_stext_set_point(GTK_STEXT(compose
->text
), 0);
1105 gtk_stext_thaw(text
);
1106 gtk_widget_grab_focus(compose
->header_last
->entry
);
1108 #if 0 /* NEW COMPOSE GUI */
1109 if (account
->protocol
!= A_NNTP
)
1110 gtk_widget_grab_focus(compose
->to_entry
);
1112 gtk_widget_grab_focus(compose
->newsgroups_entry
);
1118 void compose_reedit(MsgInfo
*msginfo
)
1121 PrefsAccount
*account
= NULL
;
1124 gchar buf
[BUFFSIZE
];
1126 g_return_if_fail(msginfo
!= NULL
);
1127 g_return_if_fail(msginfo
->folder
!= NULL
);
1129 if (msginfo
->folder
->stype
== F_QUEUE
|| msginfo
->folder
->stype
== F_DRAFT
) {
1130 gchar queueheader_buf
[BUFFSIZE
];
1133 /* Select Account from queue headers */
1134 if (!get_header_from_msginfo(msginfo
, queueheader_buf
,
1135 sizeof(queueheader_buf
), "X-Sylpheed-Account-Id:")) {
1136 id
= atoi(&queueheader_buf
[22]);
1137 account
= account_find_from_id(id
);
1139 if (!account
&& !get_header_from_msginfo(msginfo
, queueheader_buf
,
1140 sizeof(queueheader_buf
), "NAID:")) {
1141 id
= atoi(&queueheader_buf
[5]);
1142 account
= account_find_from_id(id
);
1144 if (!account
&& !get_header_from_msginfo(msginfo
, queueheader_buf
,
1145 sizeof(queueheader_buf
), "MAID:")) {
1146 id
= atoi(&queueheader_buf
[5]);
1147 account
= account_find_from_id(id
);
1149 if (!account
&& !get_header_from_msginfo(msginfo
, queueheader_buf
,
1150 sizeof(queueheader_buf
), "S:")) {
1151 account
= account_find_from_address(queueheader_buf
);
1154 account
= msginfo
->folder
->folder
->account
;
1156 if (!account
&& prefs_common
.reedit_account_autosel
) {
1157 gchar from
[BUFFSIZE
];
1158 if (!get_header_from_msginfo(msginfo
, from
, sizeof(from
), "FROM:")){
1159 extract_address(from
);
1160 account
= account_find_from_address(from
);
1163 if (!account
) account
= cur_account
;
1164 g_return_if_fail(account
!= NULL
);
1166 compose
= compose_create(account
, COMPOSE_REEDIT
);
1167 compose
->targetinfo
= procmsg_msginfo_copy(msginfo
);
1169 if (msginfo
->folder
->stype
== F_QUEUE
1170 || msginfo
->folder
->stype
== F_DRAFT
) {
1171 gchar queueheader_buf
[BUFFSIZE
];
1173 /* Set message save folder */
1174 if (!get_header_from_msginfo(msginfo
, queueheader_buf
, sizeof(queueheader_buf
), "SCF:")) {
1177 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
), TRUE
);
1178 gtk_editable_delete_text(GTK_EDITABLE(compose
->savemsg_entry
), 0, -1);
1179 gtk_editable_insert_text(GTK_EDITABLE(compose
->savemsg_entry
), &queueheader_buf
[4], strlen(&queueheader_buf
[4]), &startpos
);
1183 if (compose_parse_header(compose
, msginfo
) < 0) return;
1184 compose_reedit_set_entry(compose
, msginfo
);
1186 text
= GTK_STEXT(compose
->text
);
1187 gtk_stext_freeze(text
);
1189 if ((fp
= procmime_get_first_text_content(msginfo
)) == NULL
)
1190 g_warning("Can't get text part\n");
1192 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1194 gtk_stext_insert(text
, NULL
, NULL
, NULL
, buf
, -1);
1198 compose_attach_parts(compose
, msginfo
);
1200 gtk_stext_thaw(text
);
1201 gtk_widget_grab_focus(compose
->text
);
1203 if (prefs_common
.auto_exteditor
)
1204 compose_exec_ext_editor(compose
);
1207 Compose
*compose_redirect(PrefsAccount
*account
, MsgInfo
*msginfo
)
1211 GtkItemFactory
*ifactory
;
1213 g_return_val_if_fail(msginfo
!= NULL
, NULL
);
1216 account
= cur_account
;
1217 g_return_val_if_fail(account
!= NULL
, NULL
);
1219 compose
= compose_create(account
, COMPOSE_REDIRECT
);
1220 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1222 compose
->replyinfo
= NULL
;
1224 compose_show_first_last_header(compose
, TRUE
);
1226 gtk_widget_grab_focus(compose
->header_last
->entry
);
1228 filename
= procmsg_get_message_file(msginfo
);
1229 if (filename
== NULL
)
1232 compose
->redirect_filename
= filename
;
1234 compose_attach_parts(compose
, msginfo
);
1236 if (msginfo
->subject
)
1237 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
1239 gtk_editable_set_editable(GTK_EDITABLE(compose
->subject_entry
), FALSE
);
1241 gtk_stext_freeze(GTK_STEXT(compose
->text
));
1242 compose_quote_fmt(compose
, msginfo
, "%M", NULL
, NULL
);
1243 gtk_editable_set_editable(GTK_EDITABLE(compose
->text
), FALSE
);
1244 gtk_stext_thaw(GTK_STEXT(compose
->text
));
1246 ifactory
= gtk_item_factory_from_widget(compose
->popupmenu
);
1247 menu_set_sensitive(ifactory
, "/Add...", FALSE
);
1248 menu_set_sensitive(ifactory
, "/Remove", FALSE
);
1249 menu_set_sensitive(ifactory
, "/Property...", FALSE
);
1251 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1252 menu_set_sensitive(ifactory
, "/File/Insert file", FALSE
);
1253 menu_set_sensitive(ifactory
, "/File/Attach file", FALSE
);
1254 menu_set_sensitive(ifactory
, "/File/Insert signature", FALSE
);
1255 menu_set_sensitive(ifactory
, "/Edit", FALSE
);
1256 menu_set_sensitive(ifactory
, "/Message/Save to draft folder", FALSE
);
1257 menu_set_sensitive(ifactory
, "/Message/Save and keep editing", FALSE
);
1259 menu_set_sensitive(ifactory
, "/Message/Sign", FALSE
);
1260 menu_set_sensitive(ifactory
, "/Message/Encrypt", FALSE
);
1261 menu_set_sensitive(ifactory
, "/Message/Mode/MIME", FALSE
);
1262 menu_set_sensitive(ifactory
, "/Message/Mode/Inline", FALSE
);
1264 menu_set_sensitive(ifactory
, "/Message/Priority", FALSE
);
1265 menu_set_sensitive(ifactory
, "/Message/Request Return Receipt", FALSE
);
1266 menu_set_sensitive(ifactory
, "/Tools/Show ruler", FALSE
);
1267 menu_set_sensitive(ifactory
, "/Tools/Actions", FALSE
);
1269 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, FALSE
);
1270 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, FALSE
);
1271 gtk_widget_set_sensitive(compose
->toolbar
->attach_btn
, FALSE
);
1272 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, FALSE
);
1273 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, FALSE
);
1274 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_btn
, FALSE
);
1279 GList
*compose_get_compose_list(void)
1281 return compose_list
;
1284 void compose_entry_append(Compose
*compose
, const gchar
*address
,
1285 ComposeEntryType type
)
1289 if (!address
|| *address
== '\0') return;
1291 #if 0 /* NEW COMPOSE GUI */
1294 entry
= GTK_ENTRY(compose
->cc_entry
);
1297 entry
= GTK_ENTRY(compose
->bcc_entry
);
1299 case COMPOSE_NEWSGROUPS
:
1300 entry
= GTK_ENTRY(compose
->newsgroups_entry
);
1304 entry
= GTK_ENTRY(compose
->to_entry
);
1308 text
= gtk_entry_get_text(entry
);
1310 gtk_entry_append_text(entry
, ", ");
1311 gtk_entry_append_text(entry
, address
);
1319 header
= N_("Bcc:");
1321 case COMPOSE_REPLYTO
:
1322 header
= N_("Reply-To:");
1324 case COMPOSE_NEWSGROUPS
:
1325 header
= N_("Newsgroups:");
1327 case COMPOSE_FOLLOWUPTO
:
1328 header
= N_( "Followup-To:");
1335 header
= prefs_common
.trans_hdr
? gettext(header
) : header
;
1337 compose_add_header_entry(compose
, header
, (gchar
*)address
);
1340 void compose_entry_select (Compose
*compose
, const gchar
*mailto
)
1342 GSList
*header_list
;
1344 for (header_list
= compose
->header_list
; header_list
!= NULL
; header_list
= header_list
->next
) {
1345 GtkEntry
* entry
= GTK_ENTRY(((ComposeHeaderEntry
*)header_list
->data
)->entry
);
1347 if (gtk_entry_get_text(entry
) && !g_strcasecmp(gtk_entry_get_text(entry
), mailto
)) {
1348 gtk_entry_select_region(entry
, 0, -1);
1349 gtk_widget_grab_focus(GTK_WIDGET(entry
));
1354 void compose_toolbar_cb(gint action
, gpointer data
)
1356 ToolbarItem
*toolbar_item
= (ToolbarItem
*)data
;
1357 Compose
*compose
= (Compose
*)toolbar_item
->parent
;
1359 g_return_if_fail(compose
!= NULL
);
1363 compose_send_cb(compose
, 0, NULL
);
1366 compose_send_later_cb(compose
, 0, NULL
);
1369 compose_draft_cb(compose
, 0, NULL
);
1372 compose_insert_file_cb(compose
, 0, NULL
);
1375 compose_attach_cb(compose
, 0, NULL
);
1378 compose_insert_sig(compose
);
1381 compose_ext_editor_cb(compose
, 0, NULL
);
1384 compose_wrap_line(compose
);
1387 compose_address_cb(compose
, 0, NULL
);
1394 static void compose_entries_set(Compose
*compose
, const gchar
*mailto
)
1399 gchar
*subject
= NULL
;
1402 scan_mailto_url(mailto
, &to
, &cc
, &bcc
, &subject
, &body
);
1405 compose_entry_append(compose
, to
, COMPOSE_TO
);
1407 compose_entry_append(compose
, cc
, COMPOSE_CC
);
1409 compose_entry_append(compose
, bcc
, COMPOSE_BCC
);
1411 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), subject
);
1413 gtk_stext_insert(GTK_STEXT(compose
->text
),
1414 NULL
, NULL
, NULL
, body
, -1);
1415 gtk_stext_insert(GTK_STEXT(compose
->text
),
1416 NULL
, NULL
, NULL
, "\n", 1);
1426 static gint
compose_parse_header(Compose
*compose
, MsgInfo
*msginfo
)
1428 static HeaderEntry hentry
[] = {{"Reply-To:", NULL
, TRUE
},
1429 {"Cc:", NULL
, FALSE
},
1430 {"References:", NULL
, FALSE
},
1431 {"Bcc:", NULL
, FALSE
},
1432 {"Newsgroups:", NULL
, FALSE
},
1433 {"Followup-To:", NULL
, FALSE
},
1434 {"List-Post:", NULL
, FALSE
},
1435 {"X-Priority:", NULL
, FALSE
},
1436 {NULL
, NULL
, FALSE
}};
1452 g_return_val_if_fail(msginfo
!= NULL
, -1);
1454 if ((fp
= procmsg_open_message(msginfo
)) == NULL
) return -1;
1455 procheader_get_header_fields(fp
, hentry
);
1458 if (hentry
[H_REPLY_TO
].body
!= NULL
) {
1459 conv_unmime_header_overwrite(hentry
[H_REPLY_TO
].body
);
1460 compose
->replyto
= hentry
[H_REPLY_TO
].body
;
1461 hentry
[H_REPLY_TO
].body
= NULL
;
1463 if (hentry
[H_CC
].body
!= NULL
) {
1464 conv_unmime_header_overwrite(hentry
[H_CC
].body
);
1465 compose
->cc
= hentry
[H_CC
].body
;
1466 hentry
[H_CC
].body
= NULL
;
1468 if (hentry
[H_REFERENCES
].body
!= NULL
) {
1469 if (compose
->mode
== COMPOSE_REEDIT
)
1470 compose
->references
= hentry
[H_REFERENCES
].body
;
1472 compose
->references
= compose_parse_references
1473 (hentry
[H_REFERENCES
].body
, msginfo
->msgid
);
1474 g_free(hentry
[H_REFERENCES
].body
);
1476 hentry
[H_REFERENCES
].body
= NULL
;
1478 if (hentry
[H_BCC
].body
!= NULL
) {
1479 if (compose
->mode
== COMPOSE_REEDIT
) {
1480 conv_unmime_header_overwrite(hentry
[H_BCC
].body
);
1481 compose
->bcc
= hentry
[H_BCC
].body
;
1483 g_free(hentry
[H_BCC
].body
);
1484 hentry
[H_BCC
].body
= NULL
;
1486 if (hentry
[H_NEWSGROUPS
].body
!= NULL
) {
1487 compose
->newsgroups
= hentry
[H_NEWSGROUPS
].body
;
1488 hentry
[H_NEWSGROUPS
].body
= NULL
;
1490 if (hentry
[H_FOLLOWUP_TO
].body
!= NULL
) {
1491 conv_unmime_header_overwrite(hentry
[H_FOLLOWUP_TO
].body
);
1492 compose
->followup_to
= hentry
[H_FOLLOWUP_TO
].body
;
1493 hentry
[H_FOLLOWUP_TO
].body
= NULL
;
1495 if (hentry
[H_LIST_POST
].body
!= NULL
) {
1498 extract_address(hentry
[H_LIST_POST
].body
);
1499 if (hentry
[H_LIST_POST
].body
[0] != '\0') {
1500 scan_mailto_url(hentry
[H_LIST_POST
].body
,
1501 &to
, NULL
, NULL
, NULL
, NULL
);
1503 g_free(compose
->ml_post
);
1504 compose
->ml_post
= to
;
1507 g_free(hentry
[H_LIST_POST
].body
);
1508 hentry
[H_LIST_POST
].body
= NULL
;
1511 /* CLAWS - X-Priority */
1512 if (compose
->mode
== COMPOSE_REEDIT
)
1513 if (hentry
[H_X_PRIORITY
].body
!= NULL
) {
1516 priority
= atoi(hentry
[H_X_PRIORITY
].body
);
1517 g_free(hentry
[H_X_PRIORITY
].body
);
1519 hentry
[H_X_PRIORITY
].body
= NULL
;
1521 if (priority
< PRIORITY_HIGHEST
||
1522 priority
> PRIORITY_LOWEST
)
1523 priority
= PRIORITY_NORMAL
;
1525 compose
->priority
= priority
;
1528 if (compose
->mode
== COMPOSE_REEDIT
&& msginfo
->inreplyto
)
1529 compose
->inreplyto
= g_strdup(msginfo
->inreplyto
);
1530 else if (compose
->mode
!= COMPOSE_REEDIT
&&
1531 msginfo
->msgid
&& *msginfo
->msgid
) {
1532 compose
->inreplyto
= g_strdup(msginfo
->msgid
);
1534 if (!compose
->references
) {
1535 if (msginfo
->inreplyto
&& *msginfo
->inreplyto
)
1536 compose
->references
=
1537 g_strdup_printf("<%s>\n\t<%s>",
1541 compose
->references
=
1542 g_strconcat("<", msginfo
->msgid
, ">",
1550 static gchar
*compose_parse_references(const gchar
*ref
, const gchar
*msgid
)
1552 GSList
*ref_id_list
, *cur
;
1556 ref_id_list
= references_list_append(NULL
, ref
);
1557 if (!ref_id_list
) return NULL
;
1558 if (msgid
&& *msgid
)
1559 ref_id_list
= g_slist_append(ref_id_list
, g_strdup(msgid
));
1564 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
)
1565 /* "<" + Message-ID + ">" + CR+LF+TAB */
1566 len
+= strlen((gchar
*)cur
->data
) + 5;
1568 if (len
> MAX_REFERENCES_LEN
) {
1569 /* remove second message-ID */
1570 if (ref_id_list
&& ref_id_list
->next
&&
1571 ref_id_list
->next
->next
) {
1572 g_free(ref_id_list
->next
->data
);
1573 ref_id_list
= g_slist_remove
1574 (ref_id_list
, ref_id_list
->next
->data
);
1576 slist_free_strings(ref_id_list
);
1577 g_slist_free(ref_id_list
);
1584 new_ref
= g_string_new("");
1585 for (cur
= ref_id_list
; cur
!= NULL
; cur
= cur
->next
) {
1586 if (new_ref
->len
> 0)
1587 g_string_append(new_ref
, "\n\t");
1588 g_string_sprintfa(new_ref
, "<%s>", (gchar
*)cur
->data
);
1591 slist_free_strings(ref_id_list
);
1592 g_slist_free(ref_id_list
);
1594 new_ref_str
= new_ref
->str
;
1595 g_string_free(new_ref
, FALSE
);
1600 static gchar
*compose_quote_fmt(Compose
*compose
, MsgInfo
*msginfo
,
1601 const gchar
*fmt
, const gchar
*qmark
,
1604 GtkSText
*text
= GTK_STEXT(compose
->text
);
1605 static MsgInfo dummyinfo
;
1606 gchar
*quote_str
= NULL
;
1612 msginfo
= &dummyinfo
;
1614 if (qmark
!= NULL
) {
1615 quote_fmt_init(msginfo
, NULL
, NULL
);
1616 quote_fmt_scan_string(qmark
);
1619 buf
= quote_fmt_get_buffer();
1621 alertpanel_error(_("Quote mark format error."));
1623 Xstrdup_a(quote_str
, buf
, return NULL
)
1626 if (fmt
&& *fmt
!= '\0') {
1627 quote_fmt_init(msginfo
, quote_str
, body
);
1628 quote_fmt_scan_string(fmt
);
1631 buf
= quote_fmt_get_buffer();
1633 alertpanel_error(_("Message reply/forward format error."));
1639 gtk_stext_freeze(text
);
1641 for (p
= buf
; *p
!= '\0'; ) {
1642 lastp
= strchr(p
, '\n');
1643 len
= lastp
? lastp
- p
+ 1 : -1;
1644 gtk_stext_insert(text
, NULL
, NULL
, NULL
, p
, len
);
1651 gtk_stext_thaw(text
);
1656 static void compose_reply_set_entry(Compose
*compose
, MsgInfo
*msginfo
,
1657 gboolean to_all
, gboolean to_ml
,
1658 gboolean ignore_replyto
,
1659 gboolean followup_and_reply_to
)
1661 GSList
*cc_list
= NULL
;
1664 gchar
*replyto
= NULL
;
1665 GHashTable
*to_table
;
1667 g_return_if_fail(compose
->account
!= NULL
);
1668 g_return_if_fail(msginfo
!= NULL
);
1670 if (compose
->account
->protocol
!= A_NNTP
|| followup_and_reply_to
) {
1671 if (!compose
->replyto
&& to_ml
&& compose
->ml_post
1672 && !(msginfo
->folder
&& msginfo
->folder
->prefs
->enable_default_reply_to
))
1673 compose_entry_append(compose
,
1676 else if (!(to_all
|| ignore_replyto
)
1678 && msginfo
->folder
->prefs
->enable_default_reply_to
) {
1679 compose_entry_append(compose
,
1680 msginfo
->folder
->prefs
->default_reply_to
,
1683 compose_entry_append(compose
,
1684 (compose
->replyto
&& !ignore_replyto
)
1686 : msginfo
->from
? msginfo
->from
: "",
1690 compose_entry_append
1691 (compose
, msginfo
->from
? msginfo
->from
: "",
1694 if (compose
->followup_to
&& !strncmp(compose
->followup_to
, "poster", 6)) {
1695 compose_entry_append
1697 ((compose
->replyto
&& !ignore_replyto
)
1699 : msginfo
->from
? msginfo
->from
: ""),
1702 compose_entry_append
1704 compose
->followup_to
? compose
->followup_to
1705 : compose
->newsgroups
? compose
->newsgroups
1707 COMPOSE_NEWSGROUPS
);
1712 if (msginfo
->subject
&& *msginfo
->subject
) {
1713 gchar
*buf
, *buf2
, *p
;
1715 buf
= g_strdup(msginfo
->subject
);
1716 while (!strncasecmp(buf
, "Re:", 3)) {
1718 while (isspace(*p
)) p
++;
1719 memmove(buf
, p
, strlen(p
) + 1);
1722 buf2
= g_strdup_printf("Re: %s", buf
);
1723 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), buf2
);
1727 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
), "Re: ");
1729 if (to_ml
&& compose
->ml_post
) return;
1730 if (!to_all
|| compose
->account
->protocol
== A_NNTP
) return;
1732 if (compose
->replyto
) {
1733 Xstrdup_a(replyto
, compose
->replyto
, return);
1734 extract_address(replyto
);
1736 if (msginfo
->from
) {
1737 Xstrdup_a(from
, msginfo
->from
, return);
1738 extract_address(from
);
1741 if (replyto
&& from
)
1742 cc_list
= address_list_append(cc_list
, from
);
1743 cc_list
= address_list_append(cc_list
, msginfo
->to
);
1744 cc_list
= address_list_append(cc_list
, compose
->cc
);
1746 to_table
= g_hash_table_new(g_str_hash
, g_str_equal
);
1748 g_hash_table_insert(to_table
, replyto
, GINT_TO_POINTER(1));
1749 if (compose
->account
)
1750 g_hash_table_insert(to_table
, compose
->account
->address
,
1751 GINT_TO_POINTER(1));
1753 /* remove address on To: and that of current account */
1754 for (cur
= cc_list
; cur
!= NULL
; ) {
1755 GSList
*next
= cur
->next
;
1757 if (g_hash_table_lookup(to_table
, cur
->data
) != NULL
)
1758 cc_list
= g_slist_remove(cc_list
, cur
->data
);
1760 g_hash_table_insert(to_table
, cur
->data
, cur
);
1764 g_hash_table_destroy(to_table
);
1767 for (cur
= cc_list
; cur
!= NULL
; cur
= cur
->next
)
1768 compose_entry_append(compose
, (gchar
*)cur
->data
,
1770 slist_free_strings(cc_list
);
1771 g_slist_free(cc_list
);
1775 #define SET_ENTRY(entry, str) \
1778 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
1781 #define SET_ADDRESS(type, str) \
1784 compose_entry_append(compose, str, type); \
1787 static void compose_reedit_set_entry(Compose
*compose
, MsgInfo
*msginfo
)
1789 g_return_if_fail(msginfo
!= NULL
);
1791 SET_ENTRY(subject_entry
, msginfo
->subject
);
1792 SET_ADDRESS(COMPOSE_TO
, msginfo
->to
);
1793 SET_ADDRESS(COMPOSE_CC
, compose
->cc
);
1794 SET_ADDRESS(COMPOSE_BCC
, compose
->bcc
);
1795 SET_ADDRESS(COMPOSE_REPLYTO
, compose
->replyto
);
1797 compose_update_priority_menu_item(compose
);
1799 compose_update_gnupg_mode_menu_item(compose
);
1801 compose_show_first_last_header(compose
, TRUE
);
1808 static void compose_exec_sig(Compose
*compose
, gchar
*sigfile
)
1812 size_t buf_len
= 128;
1814 if (strlen(sigfile
) < 2)
1817 sigprg
= popen(sigfile
+1, "r");
1820 buf
= g_malloc(buf_len
);
1823 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, \
1824 "Unable to insert signature (malloc failed)\n", -1);
1830 while (!feof(sigprg
)) {
1831 bzero(buf
, buf_len
);
1832 fread(buf
, buf_len
-1, 1, sigprg
);
1833 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, buf
, -1);
1841 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, \
1842 "Can't exec file: ", -1);
1843 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, \
1848 static void compose_insert_sig(Compose
*compose
)
1852 if (compose
->account
&& compose
->account
->sig_path
)
1853 sigfile
= g_strdup(compose
->account
->sig_path
);
1855 sigfile
= g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S
,
1856 DEFAULT_SIGNATURE
, NULL
);
1858 if (!is_file_or_fifo_exist(sigfile
) && sigfile
[0] != '|') {
1863 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
, "\n\n", 2);
1864 if (prefs_common
.sig_sep
) {
1865 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
,
1866 prefs_common
.sig_sep
, -1);
1867 gtk_stext_insert(GTK_STEXT(compose
->text
), NULL
, NULL
, NULL
,
1871 if (sigfile
[0] == '|')
1872 compose_exec_sig(compose
, sigfile
);
1874 compose_insert_file(compose
, sigfile
);
1878 static void compose_insert_file(Compose
*compose
, const gchar
*file
)
1880 GtkSText
*text
= GTK_STEXT(compose
->text
);
1881 gchar buf
[BUFFSIZE
];
1885 g_return_if_fail(file
!= NULL
);
1887 if ((fp
= fopen(file
, "rb")) == NULL
) {
1888 FILE_OP_ERROR(file
, "fopen");
1892 gtk_stext_freeze(text
);
1894 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
1895 /* strip <CR> if DOS/Windows file,
1896 replace <CR> with <LF> if Macintosh file. */
1899 if (len
> 0 && buf
[len
- 1] != '\n') {
1901 if (buf
[len
] == '\r') buf
[len
] = '\n';
1903 gtk_stext_insert(text
, NULL
, NULL
, NULL
, buf
, -1);
1906 gtk_stext_thaw(text
);
1911 static void compose_attach_append(Compose
*compose
, const gchar
*file
,
1912 const gchar
*filename
,
1913 const gchar
*content_type
)
1916 gchar
*text
[N_ATTACH_COLS
];
1921 if (!is_file_exist(file
)) {
1922 g_warning("File %s doesn't exist\n", file
);
1925 if ((size
= get_file_size(file
)) < 0) {
1926 g_warning("Can't get file size of %s\n", file
);
1930 alertpanel_notice(_("File %s is empty."), file
);
1933 if ((fp
= fopen(file
, "rb")) == NULL
) {
1934 alertpanel_error(_("Can't read %s."), file
);
1939 #if 0 /* NEW COMPOSE GUI */
1940 if (!compose
->use_attach
) {
1941 GtkItemFactory
*ifactory
;
1942 GtkWidget
*menuitem
;
1944 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
1945 menuitem
= gtk_item_factory_get_item(ifactory
,
1946 "/View/Attachment");
1947 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
1951 ainfo
= g_new0(AttachInfo
, 1);
1952 ainfo
->file
= g_strdup(file
);
1955 ainfo
->content_type
= g_strdup(content_type
);
1956 if (!strcasecmp(content_type
, "message/rfc822")) {
1957 ainfo
->encoding
= ENC_7BIT
;
1958 ainfo
->name
= g_strdup_printf
1960 g_basename(filename
? filename
: file
));
1962 if (!g_strncasecmp(content_type
, "text", 4))
1964 procmime_get_encoding_for_file(file
);
1966 ainfo
->encoding
= ENC_BASE64
;
1967 ainfo
->name
= g_strdup
1968 (g_basename(filename
? filename
: file
));
1971 ainfo
->content_type
= procmime_get_mime_type(file
);
1972 if (!ainfo
->content_type
) {
1973 ainfo
->content_type
=
1974 g_strdup("application/octet-stream");
1975 ainfo
->encoding
= ENC_BASE64
;
1976 } else if (!g_strncasecmp(ainfo
->content_type
, "text", 4))
1977 ainfo
->encoding
= procmime_get_encoding_for_file(file
);
1979 ainfo
->encoding
= ENC_BASE64
;
1980 ainfo
->name
= g_strdup(g_basename(filename
? filename
: file
));
1984 text
[COL_MIMETYPE
] = ainfo
->content_type
;
1985 text
[COL_SIZE
] = to_human_readable(size
);
1986 text
[COL_NAME
] = ainfo
->name
;
1988 row
= gtk_clist_append(GTK_CLIST(compose
->attach_clist
), text
);
1989 gtk_clist_set_row_data(GTK_CLIST(compose
->attach_clist
), row
, ainfo
);
1992 #define IS_FIRST_PART_TEXT(info) \
1993 ((info->mime_type == MIME_TEXT || info->mime_type == MIME_TEXT_HTML || \
1994 info->mime_type == MIME_TEXT_ENRICHED) || \
1995 (info->mime_type == MIME_MULTIPART && info->content_type && \
1996 !strcasecmp(info->content_type, "multipart/alternative") && \
1997 (info->children && \
1998 (info->children->mime_type == MIME_TEXT || \
1999 info->children->mime_type == MIME_TEXT_HTML || \
2000 info->children->mime_type == MIME_TEXT_ENRICHED))))
2002 static void compose_attach_parts(Compose
*compose
, MsgInfo
*msginfo
)
2009 mimeinfo
= procmime_scan_message(msginfo
);
2010 if (!mimeinfo
) return;
2012 /* skip first text (presumably message body) */
2013 child
= mimeinfo
->children
;
2014 if (!child
|| IS_FIRST_PART_TEXT(mimeinfo
)) {
2015 procmime_mimeinfo_free_all(mimeinfo
);
2019 if (IS_FIRST_PART_TEXT(child
))
2020 child
= child
->next
;
2022 infile
= procmsg_get_message_file_path(msginfo
);
2024 while (child
!= NULL
) {
2025 if (child
->children
|| child
->mime_type
== MIME_MULTIPART
) {
2026 child
= procmime_mimeinfo_next(child
);
2029 if(child
->parent
&& child
->parent
->parent
2030 && !strcasecmp(child
->parent
->parent
->content_type
, "multipart/signed")
2031 && child
->mime_type
== MIME_TEXT
) {
2032 /* this is the main text part of a signed message */
2033 child
= procmime_mimeinfo_next(child
);
2036 outfile
= procmime_get_tmp_file_name(child
);
2037 if (procmime_get_part(outfile
, infile
, child
) < 0)
2038 g_warning("Can't get the part of multipart message.");
2039 else if (compose
->mode
!= COMPOSE_REEDIT
|| strcmp(child
->content_type
, "application/pgp-signature"))
2040 compose_attach_append
2042 child
->filename
? child
->filename
: child
->name
,
2043 child
->content_type
);
2045 child
= child
->next
;
2049 procmime_mimeinfo_free_all(mimeinfo
);
2052 #undef IS_FIRST_PART_TEXT
2054 #define GET_CHAR(pos, buf, len) \
2056 if (text->use_wchar) \
2057 len = wctomb(buf, (wchar_t)GTK_STEXT_INDEX(text, (pos))); \
2059 buf[0] = GTK_STEXT_INDEX(text, (pos)); \
2064 #define INDENT_CHARS ">|#"
2065 #define SPACE_CHARS " \t"
2067 static void compose_wrap_line(Compose
*compose
)
2069 GtkSText
*text
= GTK_STEXT(compose
->text
);
2070 gint ch_len
, last_ch_len
;
2071 gchar cbuf
[MB_LEN_MAX
], last_ch
;
2075 gint p_start
, p_end
;
2076 gint line_pos
, cur_pos
;
2077 gint line_len
, cur_len
;
2079 gtk_stext_freeze(text
);
2081 text_len
= gtk_stext_get_length(text
);
2083 /* check to see if the point is on the paragraph mark (empty line). */
2084 cur_pos
= gtk_stext_get_point(text
);
2085 GET_CHAR(cur_pos
, cbuf
, ch_len
);
2086 if ((ch_len
== 1 && *cbuf
== '\n') || cur_pos
== text_len
) {
2088 goto compose_end
; /* on the paragraph mark */
2089 GET_CHAR(cur_pos
- 1, cbuf
, ch_len
);
2090 if (ch_len
== 1 && *cbuf
== '\n')
2091 goto compose_end
; /* on the paragraph mark */
2094 /* find paragraph start. */
2095 line_end
= quoted
= 0;
2096 for (p_start
= cur_pos
; p_start
>= 0; --p_start
) {
2097 GET_CHAR(p_start
, cbuf
, ch_len
);
2098 if (ch_len
== 1 && *cbuf
== '\n') {
2100 goto compose_end
; /* quoted part */
2108 && strchr(prefs_common
.quote_chars
, *cbuf
))
2110 else if (ch_len
!= 1 || !isspace(*cbuf
))
2119 /* find paragraph end. */
2121 for (p_end
= cur_pos
; p_end
< text_len
; p_end
++) {
2122 GET_CHAR(p_end
, cbuf
, ch_len
);
2123 if (ch_len
== 1 && *cbuf
== '\n') {
2130 if (line_end
&& ch_len
== 1 &&
2131 strchr(prefs_common
.quote_chars
, *cbuf
))
2132 goto compose_end
; /* quoted part */
2137 if (p_end
>= text_len
)
2140 if (p_start
>= p_end
)
2143 line_len
= cur_len
= 0;
2147 for (cur_pos
= p_start
; cur_pos
< p_end
; cur_pos
++) {
2150 GET_CHAR(cur_pos
, cbuf
, ch_len
);
2157 if (ch_len
== 1 && isspace(*cbuf
))
2160 if (ch_len
== 1 && *cbuf
== '\n') {
2162 if (last_ch_len
== 1 && !isspace(last_ch
)) {
2163 if (cur_pos
+ 1 < p_end
) {
2164 GET_CHAR(cur_pos
+ 1, cbuf
, ch_len
);
2165 if (ch_len
== 1 && !isspace(*cbuf
))
2169 gtk_stext_set_point(text
, cur_pos
+ 1);
2170 gtk_stext_backward_delete(text
, 1);
2172 gtk_stext_set_point(text
, cur_pos
);
2173 gtk_stext_insert(text
, NULL
, NULL
, NULL
, " ", 1);
2183 last_ch_len
= ch_len
;
2187 line_pos
= cur_pos
+ 1;
2188 line_len
= cur_len
+ ch_len
;
2191 if (cur_len
+ ch_len
> prefs_common
.linewrap_len
&&
2195 GET_CHAR(line_pos
- 1, cbuf
, ch_len
);
2196 if (ch_len
== 1 && isspace(*cbuf
)) {
2197 gtk_stext_set_point(text
, line_pos
);
2198 gtk_stext_backward_delete(text
, 1);
2207 gtk_stext_set_point(text
, line_pos
);
2208 gtk_stext_insert(text
, NULL
, NULL
, NULL
, "\n", 1);
2212 cur_len
= cur_len
- line_len
+ ch_len
;
2218 line_pos
= cur_pos
+ 1;
2219 line_len
= cur_len
+ ch_len
;
2225 gtk_stext_thaw(text
);
2230 /* Darko: used when I debug wrapping */
2231 void dump_text(GtkSText
*text
, int pos
, int tlen
, int breakoncr
)
2234 gchar cbuf
[MB_LEN_MAX
];
2236 printf("%d [", pos
);
2237 for (i
= pos
; i
< tlen
; i
++) {
2238 GET_CHAR(i
, cbuf
, clen
);
2239 if (clen
< 0) break;
2240 if (breakoncr
&& clen
== 1 && cbuf
[0] == '\n')
2242 fwrite(cbuf
, clen
, 1, stdout
);
2250 WAIT_FOR_INDENT_CHAR
,
2251 WAIT_FOR_INDENT_CHAR_OR_SPACE
2254 /* return indent length, we allow:
2255 > followed by spaces/tabs
2256 | followed by spaces/tabs
2257 uppercase characters immediately followed by >,
2258 and the repeating sequences of the above */
2259 /* return indent length */
2260 static guint
get_indent_length(GtkSText
*text
, guint start_pos
, guint text_len
)
2263 guint i
, ch_len
, alnum_cnt
= 0;
2264 IndentState state
= WAIT_FOR_INDENT_CHAR
;
2265 gchar cbuf
[MB_LEN_MAX
];
2269 for (i
= start_pos
; i
< text_len
; i
++) {
2270 GET_CHAR(i
, cbuf
, ch_len
);
2274 if (cbuf
[0] == '\n')
2277 is_indent
= strchr(prefs_common
.quote_chars
, cbuf
[0]) ? TRUE
: FALSE
;
2278 is_space
= strchr(SPACE_CHARS
, cbuf
[0]) ? TRUE
: FALSE
;
2281 case WAIT_FOR_SPACE
:
2282 if (is_space
== FALSE
)
2284 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
2286 case WAIT_FOR_INDENT_CHAR_OR_SPACE
:
2287 if (is_indent
== FALSE
&& is_space
== FALSE
&&
2290 if (is_space
== TRUE
) {
2292 state
= WAIT_FOR_INDENT_CHAR_OR_SPACE
;
2293 } else if (is_indent
== TRUE
) {
2295 state
= WAIT_FOR_SPACE
;
2298 state
= WAIT_FOR_INDENT_CHAR
;
2301 case WAIT_FOR_INDENT_CHAR
:
2302 if (is_indent
== FALSE
&& !isupper(cbuf
[0]))
2304 if (is_indent
== TRUE
) {
2306 && !strchr(prefs_common
.quote_chars
, cbuf
[0]))
2309 state
= WAIT_FOR_SPACE
;
2320 if ((i_len
> 0) && (state
== WAIT_FOR_INDENT_CHAR
))
2326 /* insert quotation string when line was wrapped */
2327 static guint
ins_quote(GtkSText
*text
, guint indent_len
,
2328 guint prev_line_pos
, guint text_len
,
2331 guint i
, ins_len
= 0;
2335 for (i
= 0; i
< indent_len
; i
++) {
2336 ch
= GTK_STEXT_INDEX(text
, prev_line_pos
+ i
);
2337 gtk_stext_insert(text
, NULL
, NULL
, NULL
, &ch
, 1);
2339 ins_len
= indent_len
;
2345 /* check if we should join the next line */
2346 static gboolean
join_next_line(GtkSText
*text
, guint start_pos
, guint tlen
,
2347 guint prev_ilen
, gboolean autowrap
)
2349 guint indent_len
, ch_len
;
2350 gboolean do_join
= FALSE
;
2351 gchar cbuf
[MB_LEN_MAX
];
2353 indent_len
= get_indent_length(text
, start_pos
, tlen
);
2355 if ((autowrap
|| indent_len
> 0) && indent_len
== prev_ilen
) {
2356 GET_CHAR(start_pos
+ indent_len
, cbuf
, ch_len
);
2357 if (ch_len
> 0 && (cbuf
[0] != '\n'))
2364 static void compose_wrap_line_all(Compose
*compose
)
2366 compose_wrap_line_all_full(compose
, FALSE
);
2369 #define STEXT_FREEZE() \
2370 if (!frozen) { gtk_stext_freeze(text); frozen = TRUE; }
2372 static void compose_wrap_line_all_full(Compose
*compose
, gboolean autowrap
)
2374 GtkSText
*text
= GTK_STEXT(compose
->text
);
2376 guint line_pos
= 0, cur_pos
= 0, p_pos
= 0;
2377 gint line_len
= 0, cur_len
= 0;
2379 gboolean is_new_line
= TRUE
, do_delete
= FALSE
;
2381 gboolean linewrap_quote
= TRUE
;
2382 gboolean set_editable_pos
= FALSE
;
2383 gint editable_pos
= 0;
2384 gboolean frozen
= FALSE
;
2385 guint linewrap_len
= prefs_common
.linewrap_len
;
2386 gchar
*qfmt
= prefs_common
.quotemark
;
2387 gchar cbuf
[MB_LEN_MAX
];
2389 tlen
= gtk_stext_get_length(text
);
2391 for (; cur_pos
< tlen
; cur_pos
++) {
2392 /* mark position of new line - needed for quotation wrap */
2395 i_len
= get_indent_length(text
, cur_pos
, tlen
);
2397 is_new_line
= FALSE
;
2400 g_print("new line i_len=%d p_pos=", i_len
);
2401 dump_text(text
, p_pos
, tlen
, 1);
2405 GET_CHAR(cur_pos
, cbuf
, ch_len
);
2407 /* fix line length for tabs */
2408 if (ch_len
== 1 && *cbuf
== '\t') {
2409 guint tab_width
= text
->default_tab_width
;
2410 guint tab_offset
= line_len
% tab_width
;
2413 g_print("found tab at pos=%d line_len=%d ", cur_pos
,
2417 line_len
+= tab_width
- tab_offset
- 1;
2421 printf("new_len=%d\n", line_len
);
2425 /* we have encountered line break */
2426 if (ch_len
== 1 && *cbuf
== '\n') {
2428 gchar cb
[MB_LEN_MAX
];
2430 /* should we join the next line */
2431 if ((autowrap
|| i_len
!= cur_len
) && do_delete
&&
2433 (text
, cur_pos
+ 1, tlen
, i_len
, autowrap
))
2439 g_print("found CR at %d do_del is %d next line is ",
2440 cur_pos
, do_delete
);
2441 dump_text(text
, cur_pos
+ 1, tlen
, 1);
2444 /* skip delete if it is continuous URL */
2445 if (do_delete
&& (line_pos
- p_pos
<= i_len
) &&
2446 gtk_stext_is_uri_string(text
, line_pos
, tlen
))
2450 g_print("l_len=%d wrap_len=%d do_del=%d\n",
2451 line_len
, linewrap_len
, do_delete
);
2453 /* should we delete to perform smart wrapping */
2454 if (line_len
< linewrap_len
&& do_delete
) {
2456 /* get rid of newline */
2457 gtk_stext_set_point(text
, cur_pos
);
2458 gtk_stext_forward_delete(text
, 1);
2461 /* if text starts with quote fmt or with
2462 indent string, delete them */
2465 ilen
= gtk_stext_str_compare_n
2466 (text
, cur_pos
, p_pos
, i_len
,
2469 gtk_stext_forward_delete
2475 GET_CHAR(cur_pos
, cb
, clen
);
2477 /* insert space if it's alphanumeric */
2478 if ((cur_pos
!= line_pos
) &&
2479 ((clen
> 1) || isalnum(cb
[0]))) {
2480 gtk_stext_insert(text
, NULL
, NULL
,
2485 /* and start over with current line */
2486 cur_pos
= p_pos
- 1;
2488 line_len
= cur_len
= 0;
2492 g_print("after delete l_pos=");
2493 dump_text(text
, line_pos
, tlen
, 1);
2498 /* mark new line beginning */
2499 line_pos
= cur_pos
+ 1;
2500 line_len
= cur_len
= 0;
2511 /* possible line break */
2512 if (ch_len
== 1 && isspace(*cbuf
)) {
2513 line_pos
= cur_pos
+ 1;
2514 line_len
= cur_len
+ ch_len
;
2517 /* are we over wrapping length set in preferences ? */
2518 if (cur_len
+ ch_len
> linewrap_len
) {
2522 g_print("should wrap cur_pos=%d ", cur_pos
);
2523 dump_text(text
, p_pos
, tlen
, 1);
2524 dump_text(text
, line_pos
, tlen
, 1);
2526 /* force wrapping if it is one long word but not URL */
2527 if (line_pos
- p_pos
<= i_len
)
2528 if (!gtk_stext_is_uri_string
2529 (text
, line_pos
, tlen
))
2530 line_pos
= cur_pos
- 1;
2532 g_print("new line_pos=%d\n", line_pos
);
2535 GET_CHAR(line_pos
- 1, cbuf
, clen
);
2537 /* if next character is space delete it */
2538 if (clen
== 1 && isspace(*cbuf
)) {
2539 if (p_pos
+ i_len
!= line_pos
||
2540 !gtk_stext_is_uri_string
2541 (text
, line_pos
, tlen
)) {
2543 /* workaround for correct cursor
2545 if (set_editable_pos
== FALSE
) {
2546 editable_pos
= gtk_editable_get_position(GTK_EDITABLE(text
));
2547 if (editable_pos
== line_pos
)
2548 set_editable_pos
= TRUE
;
2550 gtk_stext_set_point(text
, line_pos
);
2551 gtk_stext_backward_delete(text
, 1);
2560 /* if it is URL at beginning of line don't wrap */
2561 if (p_pos
+ i_len
== line_pos
&&
2562 gtk_stext_is_uri_string(text
, line_pos
, tlen
)) {
2564 g_print("found URL at ");
2565 dump_text(text
, line_pos
, tlen
, 1);
2572 gtk_stext_set_point(text
, line_pos
);
2573 gtk_stext_insert(text
, NULL
, NULL
, NULL
, "\n", 1);
2574 /* gtk_stext_compact_buffer(text); */
2577 /* for loop will increase it */
2578 cur_pos
= line_pos
- 1;
2579 /* start over with current line */
2581 line_len
= cur_len
= 0;
2582 if (autowrap
|| i_len
> 0)
2587 g_print("after CR insert ");
2588 dump_text(text
, line_pos
, tlen
, 1);
2589 dump_text(text
, cur_pos
, tlen
, 1);
2592 /* should we insert quotation ? */
2593 if (linewrap_quote
&& i_len
) {
2594 /* only if line is not already quoted */
2595 if (!gtk_stext_str_compare
2596 (text
, line_pos
, tlen
, qfmt
)) {
2599 if (line_pos
- p_pos
> i_len
) {
2601 (text
, i_len
, p_pos
,
2606 g_print("after quote insert ");
2607 dump_text(text
, line_pos
, tlen
, 1);
2615 line_pos
= cur_pos
+ 1;
2616 line_len
= cur_len
+ ch_len
;
2618 /* advance to next character in buffer */
2623 gtk_stext_thaw(text
);
2625 if (set_editable_pos
&& editable_pos
<= tlen
)
2626 gtk_editable_set_position(GTK_EDITABLE(text
), editable_pos
);
2632 static void compose_set_title(Compose
*compose
)
2637 edited
= compose
->modified
? _(" [Edited]") : "";
2638 if (compose
->account
&& compose
->account
->address
)
2639 str
= g_strdup_printf(_("%s - Compose message%s"),
2640 compose
->account
->address
, edited
);
2642 str
= g_strdup_printf(_("Compose message%s"), edited
);
2643 gtk_window_set_title(GTK_WINDOW(compose
->window
), str
);
2648 * compose_current_mail_account:
2650 * Find a current mail account (the currently selected account, or the
2651 * default account, if a news account is currently selected). If a
2652 * mail account cannot be found, display an error message.
2654 * Return value: Mail account, or NULL if not found.
2656 static PrefsAccount
*
2657 compose_current_mail_account(void)
2661 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
2664 ac
= account_get_default();
2665 if (!ac
|| ac
->protocol
== A_NNTP
) {
2666 alertpanel_error(_("Account for sending mail is not specified.\n"
2667 "Please select a mail account before sending."));
2674 static void compose_select_account(Compose
*compose
, PrefsAccount
*account
)
2676 GtkWidget
*menuitem
;
2677 GtkItemFactory
*ifactory
;
2679 g_return_if_fail(account
!= NULL
);
2681 compose
->account
= account
;
2683 compose_set_title(compose
);
2685 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
2687 if (account
->protocol
== A_NNTP
) {
2688 gtk_widget_show(compose
->newsgroups_hbox
);
2689 gtk_widget_show(compose
->newsgroups_entry
);
2690 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 2, 4);
2691 compose
->use_newsgroups
= TRUE
;
2693 menuitem
= gtk_item_factory_get_item(ifactory
, "/View/To");
2694 gtk_check_menu_item_set_active
2695 (GTK_CHECK_MENU_ITEM(menuitem
), FALSE
);
2696 gtk_widget_set_sensitive(menuitem
, TRUE
);
2697 menuitem
= gtk_item_factory_get_item(ifactory
, "/View/Cc");
2698 gtk_check_menu_item_set_active
2699 (GTK_CHECK_MENU_ITEM(menuitem
), FALSE
);
2700 gtk_widget_set_sensitive(menuitem
, TRUE
);
2702 menu_set_sensitive(ifactory
, "/View/Followup to", TRUE
);
2704 gtk_widget_hide(compose
->newsgroups_hbox
);
2705 gtk_widget_hide(compose
->newsgroups_entry
);
2706 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 2, 0);
2707 gtk_widget_queue_resize(compose
->table_vbox
);
2708 compose
->use_newsgroups
= FALSE
;
2710 menuitem
= gtk_item_factory_get_item(ifactory
, "/View/To");
2711 gtk_check_menu_item_set_active
2712 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
2713 gtk_widget_set_sensitive(menuitem
, FALSE
);
2714 menuitem
= gtk_item_factory_get_item(ifactory
, "/View/Cc");
2715 gtk_check_menu_item_set_active
2716 (GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
2717 gtk_widget_set_sensitive(menuitem
, FALSE
);
2719 menuitem
= gtk_item_factory_get_item(ifactory
, "/View/Followup to");
2720 gtk_check_menu_item_set_active
2721 (GTK_CHECK_MENU_ITEM(menuitem
), FALSE
);
2722 gtk_widget_set_sensitive(menuitem
, FALSE
);
2725 if (account
->set_autocc
) {
2726 compose_entry_show(compose
, COMPOSE_ENTRY_CC
);
2727 if (account
->auto_cc
&& compose
->mode
!= COMPOSE_REEDIT
)
2728 compose_entry_set(compose
, account
->auto_cc
,
2731 if (account
->set_autobcc
) {
2732 compose_entry_show(compose
, COMPOSE_ENTRY_BCC
);
2733 if (account
->auto_bcc
&& compose
->mode
!= COMPOSE_REEDIT
)
2734 compose_entry_set(compose
, account
->auto_bcc
,
2737 if (account
->set_autoreplyto
) {
2738 compose_entry_show(compose
, COMPOSE_ENTRY_REPLY_TO
);
2739 if (account
->auto_replyto
&& compose
->mode
!= COMPOSE_REEDIT
)
2740 compose_entry_set(compose
, account
->auto_replyto
,
2741 COMPOSE_ENTRY_REPLY_TO
);
2744 menuitem
= gtk_item_factory_get_item(ifactory
, "/View/Ruler");
2745 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
2746 prefs_common
.show_ruler
);
2750 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Sign");
2751 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
2752 account
->default_sign
);
2753 menuitem
= gtk_item_factory_get_item(ifactory
, "/Message/Encrypt");
2754 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
),
2755 account
->default_encrypt
);
2757 activate_gnupg_mode(compose
, account
);
2758 #endif /* USE_GPGME */
2761 gboolean
compose_check_for_valid_recipient(Compose
*compose
) {
2762 gchar
*recipient_headers_mail
[] = {"To:", "Cc:", "Bcc:", NULL
};
2763 gchar
*recipient_headers_news
[] = {"Newsgroups:", NULL
};
2764 gboolean recipient_found
= FALSE
;
2768 /* free to and newsgroup list */
2769 slist_free_strings(compose
->to_list
);
2770 g_slist_free(compose
->to_list
);
2771 compose
->to_list
= NULL
;
2773 slist_free_strings(compose
->newsgroup_list
);
2774 g_slist_free(compose
->newsgroup_list
);
2775 compose
->newsgroup_list
= NULL
;
2777 /* search header entries for to and newsgroup entries */
2778 for (list
= compose
->header_list
; list
; list
= list
->next
) {
2781 header
= gtk_editable_get_chars(GTK_EDITABLE(GTK_COMBO(((ComposeHeaderEntry
*)list
->data
)->combo
)->entry
), 0, -1);
2782 entry
= gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry
*)list
->data
)->entry
), 0, -1);
2784 if (entry
[0] != '\0') {
2785 for (strptr
= recipient_headers_mail
; *strptr
!= NULL
; strptr
++) {
2786 if (!strcmp(header
, (prefs_common
.trans_hdr
? gettext(*strptr
) : *strptr
))) {
2787 compose
->to_list
= address_list_append(compose
->to_list
, entry
);
2788 recipient_found
= TRUE
;
2791 for (strptr
= recipient_headers_news
; *strptr
!= NULL
; strptr
++) {
2792 if (!strcmp(header
, (prefs_common
.trans_hdr
? gettext(*strptr
) : *strptr
))) {
2793 compose
->newsgroup_list
= newsgroup_list_append(compose
->newsgroup_list
, entry
);
2794 recipient_found
= TRUE
;
2801 return recipient_found
;
2804 static gboolean
compose_check_entries(Compose
*compose
, gboolean check_subject
)
2808 if (compose_check_for_valid_recipient(compose
) == FALSE
) {
2809 alertpanel_error(_("Recipient is not specified."));
2813 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
2814 if (*str
== '\0' && check_subject
== TRUE
) {
2817 aval
= alertpanel(_("Send"),
2818 _("Subject is empty. Send it anyway?"),
2819 _("Yes"), _("No"), NULL
);
2820 if (aval
!= G_ALERTDEFAULT
)
2827 gint
compose_send(Compose
*compose
)
2834 if (compose_check_entries(compose
, TRUE
) == FALSE
)
2837 val
= compose_queue(compose
, &msgnum
, &folder
);
2839 alertpanel_error(_("Could not queue message for sending"));
2843 msgpath
= folder_item_fetch_msg(folder
, msgnum
);
2844 val
= procmsg_send_message_queue(msgpath
);
2847 folder_item_remove_msg(folder
, msgnum
);
2848 folder_update_item(folder
, TRUE
);
2853 #if 0 /* compose restructure */
2854 gint
compose_send(Compose
*compose
)
2856 gchar tmp
[MAXPATHLEN
+ 1];
2858 static gboolean lock
= FALSE
;
2862 g_return_val_if_fail(compose
->account
!= NULL
, -1);
2866 if (compose_check_entries(compose
, TRUE
) == FALSE
) {
2871 /* write to temporary file */
2872 g_snprintf(tmp
, sizeof(tmp
), "%s%ctmpmsg.%08x",
2873 get_tmp_dir(), G_DIR_SEPARATOR
, (gint
)compose
);
2875 if (prefs_common
.linewrap_at_send
)
2876 compose_wrap_line_all(compose
);
2878 if (compose_write_to_file(compose
, tmp
, FALSE
) < 0) {
2883 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
2884 g_warning("can't get recipient list.");
2890 if (compose
->to_list
) {
2893 #if 0 /* NEW COMPOSE GUI */
2894 if (compose
->account
->protocol
!= A_NNTP
)
2895 ac
= compose
->account
;
2897 ac
= account_find_from_address(compose
->account
->address
);
2899 if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
2902 ac
= account_get_default();
2904 if (!ac
|| ac
->protocol
== A_NNTP
) {
2905 alertpanel_error(_("Account for sending mail is not specified.\n"
2906 "Please select a mail account before sending."));
2913 ac
= compose
->account
;
2915 ok
= send_message(tmp
, ac
, compose
->to_list
);
2918 if (ok
== 0 && compose
->newsgroup_list
) {
2919 ok
= news_post(FOLDER(compose
->account
->folder
), tmp
);
2921 alertpanel_error(_("Error occurred while posting the message to %s ."),
2922 compose
->account
->nntp_server
);
2929 /* queue message if failed to send */
2931 if (prefs_common
.queue_msg
) {
2936 _("Error occurred while sending the message.\n"
2937 "Put this message into queue folder?"),
2938 _("OK"), _("Cancel"), NULL
);
2939 if (G_ALERTDEFAULT
== val
) {
2940 ok
= compose_queue(compose
, tmp
);
2942 alertpanel_error(_("Can't queue the message."));
2945 alertpanel_error(_("Error occurred while sending the message."));
2947 if (compose
->mode
== COMPOSE_REEDIT
) {
2948 compose_remove_reedit_target(compose
);
2949 if (compose
->targetinfo
)
2951 (compose
->targetinfo
->folder
, TRUE
);
2953 /* save message to outbox */
2954 if (prefs_common
.savemsg
) {
2957 outbox
= account_get_special_folder
2958 (compose
->account
, F_OUTBOX
);
2959 if (procmsg_save_to_outbox(outbox
, tmp
, FALSE
) < 0)
2961 (_("Can't save the message to Sent."));
2963 folder_update_item(outbox
, TRUE
);
2973 static gboolean
compose_use_attach(Compose
*compose
) {
2974 return(gtk_clist_get_row_data(GTK_CLIST(compose
->attach_clist
), 0) != NULL
);
2977 static gint
compose_redirect_write_headers_from_headerlist(Compose
*compose
,
2980 gchar buf
[BUFFSIZE
];
2982 gboolean first_address
;
2984 ComposeHeaderEntry
*headerentry
;
2985 gchar
*headerentryname
;
2989 debug_print("Writing redirect header\n");
2991 cc_hdr
= prefs_common
.trans_hdr
? _("Cc:") : "Cc:";
2992 to_hdr
= prefs_common
.trans_hdr
? _("To:") : "To:";
2994 first_address
= TRUE
;
2995 for (list
= compose
->header_list
; list
; list
= list
->next
) {
2996 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
2997 headerentryname
= gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry
->combo
)->entry
));
2999 if (g_strcasecmp(headerentryname
, cc_hdr
) == 0
3000 || g_strcasecmp(headerentryname
, to_hdr
) == 0) {
3001 str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
3002 Xstrdup_a(str
, str
, return -1);
3004 if (str
[0] != '\0') {
3005 compose_convert_header
3006 (buf
, sizeof(buf
), str
,
3007 strlen("Resent-To") + 2);
3008 if (first_address
) {
3009 fprintf(fp
, "Resent-To: ");
3010 first_address
= FALSE
;
3014 fprintf(fp
, "%s", buf
);
3018 /* if (!first_address) { */
3025 static gint
compose_redirect_write_headers(Compose
*compose
, FILE *fp
)
3027 gchar buf
[BUFFSIZE
];
3029 /* struct utsname utsbuf; */
3031 g_return_val_if_fail(fp
!= NULL
, -1);
3032 g_return_val_if_fail(compose
->account
!= NULL
, -1);
3033 g_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
3036 get_rfc822_date(buf
, sizeof(buf
));
3037 fprintf(fp
, "Resent-Date: %s\n", buf
);
3040 if (compose
->account
->name
&& *compose
->account
->name
) {
3041 compose_convert_header
3042 (buf
, sizeof(buf
), compose
->account
->name
,
3044 fprintf(fp
, "Resent-From: %s <%s>\n",
3045 buf
, compose
->account
->address
);
3047 fprintf(fp
, "Resent-From: %s\n", compose
->account
->address
);
3050 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
3052 Xstrdup_a(str
, str
, return -1);
3055 compose_convert_header(buf
, sizeof(buf
), str
,
3056 strlen("Subject: "));
3057 fprintf(fp
, "Subject: %s\n", buf
);
3061 /* Resent-Message-ID */
3062 if (compose
->account
->gen_msgid
) {
3063 compose_generate_msgid(compose
, buf
, sizeof(buf
));
3064 fprintf(fp
, "Resent-Message-Id: <%s>\n", buf
);
3065 compose
->msgid
= g_strdup(buf
);
3068 compose_redirect_write_headers_from_headerlist(compose
, fp
);
3070 /* separator between header and body */
3076 static gint
compose_redirect_write_to_file(Compose
*compose
, const gchar
*file
)
3081 gchar buf
[BUFFSIZE
];
3083 if ((fp
= fopen(compose
->redirect_filename
, "rb")) == NULL
) {
3084 FILE_OP_ERROR(file
, "fopen");
3088 if ((fdest
= fopen(file
, "wb")) == NULL
) {
3089 FILE_OP_ERROR(file
, "fopen");
3094 /* chmod for security */
3095 if (change_file_mode_rw(fdest
, file
) < 0) {
3096 FILE_OP_ERROR(file
, "chmod");
3097 g_warning("can't change file mode\n");
3100 while (procheader_get_unfolded_line(buf
, sizeof(buf
), fp
)) {
3101 /* should filter returnpath, delivered-to */
3102 if (g_strncasecmp(buf
, "Return-Path:",
3103 strlen("Return-Path:")) == 0 ||
3104 g_strncasecmp(buf
, "Delivered-To:",
3105 strlen("Delivered-To:")) == 0 ||
3106 g_strncasecmp(buf
, "Received:",
3107 strlen("Received:")) == 0 ||
3108 g_strncasecmp(buf
, "Subject:",
3109 strlen("Subject:")) == 0 ||
3110 g_strncasecmp(buf
, "X-UIDL:",
3111 strlen("X-UIDL:")) == 0)
3114 if (fputs(buf
, fdest
) == -1)
3117 if (!prefs_common
.redirect_keep_from
) {
3118 if (g_strncasecmp(buf
, "From:",
3119 strlen("From:")) == 0) {
3120 fputs(" (by way of ", fdest
);
3121 if (compose
->account
->name
3122 && *compose
->account
->name
) {
3123 compose_convert_header
3125 compose
->account
->name
,
3127 fprintf(fdest
, "%s <%s>",
3129 compose
->account
->address
);
3131 fprintf(fdest
, "%s",
3132 compose
->account
->address
);
3137 if (fputs("\n", fdest
) == -1)
3141 compose_redirect_write_headers(compose
, fdest
);
3143 while ((len
= fread(buf
, sizeof(gchar
), sizeof(buf
), fp
)) > 0) {
3144 if (fwrite(buf
, sizeof(gchar
), len
, fdest
) != len
) {
3145 FILE_OP_ERROR(file
, "fwrite");
3151 if (fclose(fdest
) == EOF
) {
3152 FILE_OP_ERROR(file
, "fclose");
3168 /* interfaces to rfc2015 to keep out the prefs stuff there.
3169 * returns 0 on success and -1 on error. */
3170 static gint
compose_create_signers_list(Compose
*compose
, GSList
**pkey_list
)
3172 const gchar
*key_id
= NULL
;
3175 switch (compose
->account
->sign_key
) {
3176 case SIGN_KEY_DEFAULT
:
3179 case SIGN_KEY_BY_FROM
:
3180 key_id
= compose
->account
->address
;
3182 case SIGN_KEY_CUSTOM
:
3183 key_id
= compose
->account
->sign_key_id
;
3189 key_list
= rfc2015_create_signers_list(key_id
);
3192 alertpanel_error(_("Could not find any key associated with "
3193 "currently selected key id `%s'."), key_id
);
3197 *pkey_list
= key_list
;
3201 /* clearsign message body text */
3202 static gint
compose_clearsign_text(Compose
*compose
, gchar
**text
)
3207 tmp_file
= get_tmp_file();
3208 if (str_write_to_file(*text
, tmp_file
) < 0) {
3213 if (canonicalize_file_replace(tmp_file
) < 0 ||
3214 compose_create_signers_list(compose
, &key_list
) < 0 ||
3215 rfc2015_clearsign(tmp_file
, key_list
) < 0) {
3222 *text
= file_read_to_str(tmp_file
);
3230 #endif /* USE_GPGME */
3232 static gint
compose_write_to_file(Compose
*compose
, const gchar
*file
,
3239 const gchar
*out_codeset
;
3240 EncodingType encoding
;
3242 if ((fp
= fopen(file
, "wb")) == NULL
) {
3243 FILE_OP_ERROR(file
, "fopen");
3247 /* chmod for security */
3248 if (change_file_mode_rw(fp
, file
) < 0) {
3249 FILE_OP_ERROR(file
, "chmod");
3250 g_warning("can't change file mode\n");
3253 /* get all composed text */
3254 chars
= gtk_editable_get_chars(GTK_EDITABLE(compose
->text
), 0, -1);
3255 len
= strlen(chars
);
3256 if (is_ascii_str(chars
)) {
3259 out_codeset
= CS_US_ASCII
;
3260 encoding
= ENC_7BIT
;
3262 const gchar
*src_codeset
;
3264 out_codeset
= conv_get_outgoing_charset_str();
3265 if (!strcasecmp(out_codeset
, CS_US_ASCII
))
3266 out_codeset
= CS_ISO_8859_1
;
3267 encoding
= procmime_get_encoding_for_charset(out_codeset
);
3271 compose
->use_signing
&& !compose
->gnupg_mode
&&
3272 encoding
== ENC_8BIT
)
3273 encoding
= ENC_BASE64
;
3276 src_codeset
= conv_get_current_charset_str();
3277 /* if current encoding is US-ASCII, set it the same as
3278 outgoing one to prevent code conversion failure */
3279 if (!strcasecmp(src_codeset
, CS_US_ASCII
))
3280 src_codeset
= out_codeset
;
3282 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
3283 src_codeset
, out_codeset
, procmime_get_encoding_str(encoding
));
3285 buf
= conv_codeset_strdup(chars
, src_codeset
, out_codeset
);
3291 _("Can't convert the character encoding of the message.\n"
3292 "Send it anyway?"), _("Yes"), _("+No"), NULL
);
3293 if (aval
!= G_ALERTDEFAULT
) {
3306 /* Canonicalize line endings in the message text */
3308 gchar
*canon_buf
, *out
;
3312 for (p
= buf
; *p
; ++p
) {
3320 out
= canon_buf
= g_new(gchar
, new_len
+ 1);
3321 for (p
= buf
; *p
; ++p
) {
3335 if (!is_draft
&& compose
->use_signing
&& compose
->gnupg_mode
) {
3336 if (compose_clearsign_text(compose
, &buf
) < 0) {
3337 g_warning("clearsign failed\n");
3347 if (compose_write_headers
3348 (compose
, fp
, out_codeset
, encoding
, is_draft
) < 0) {
3349 g_warning("can't write headers\n");
3356 if (compose_use_attach(compose
)) {
3358 /* This prolog message is ignored by mime software and
3359 * because it would make our signing/encryption task
3360 * tougher, we don't emit it in that case */
3361 if (!compose
->use_signing
&& !compose
->use_encryption
)
3363 fputs("This is a multi-part message in MIME format.\n", fp
);
3365 fprintf(fp
, "\n--%s\n", compose
->boundary
);
3366 fprintf(fp
, "Content-Type: text/plain; charset=%s\n",
3368 fprintf(fp
, "Content-Transfer-Encoding: %s\n",
3369 procmime_get_encoding_str(encoding
));
3375 if (encoding
== ENC_BASE64
) {
3376 gchar outbuf
[B64_BUFFSIZE
];
3379 for (i
= 0; i
< len
; i
+= B64_LINE_SIZE
) {
3380 l
= MIN(B64_LINE_SIZE
, len
- i
);
3381 base64_encode(outbuf
, buf
+ i
, l
);
3385 } else if (fwrite(buf
, sizeof(gchar
), len
, fp
) != len
) {
3386 FILE_OP_ERROR(file
, "fwrite");
3394 if (compose_use_attach(compose
))
3395 compose_write_attach(compose
, fp
);
3397 if (fclose(fp
) == EOF
) {
3398 FILE_OP_ERROR(file
, "fclose");
3407 if ((compose
->use_signing
&& !compose
->gnupg_mode
) ||
3408 compose
->use_encryption
) {
3409 if (canonicalize_file_replace(file
) < 0) {
3415 if (compose
->use_signing
&& !compose
->gnupg_mode
) {
3418 if (compose_create_signers_list(compose
, &key_list
) < 0 ||
3419 rfc2015_sign(file
, key_list
) < 0) {
3424 if (compose
->use_encryption
) {
3425 if (rfc2015_encrypt(file
, compose
->to_list
,
3426 compose
->gnupg_mode
) < 0) {
3431 #endif /* USE_GPGME */
3436 static gint
compose_write_body_to_file(Compose
*compose
, const gchar
*file
)
3442 if ((fp
= fopen(file
, "wb")) == NULL
) {
3443 FILE_OP_ERROR(file
, "fopen");
3447 /* chmod for security */
3448 if (change_file_mode_rw(fp
, file
) < 0) {
3449 FILE_OP_ERROR(file
, "chmod");
3450 g_warning("can't change file mode\n");
3453 chars
= gtk_editable_get_chars(GTK_EDITABLE(compose
->text
), 0, -1);
3456 len
= strlen(chars
);
3457 if (fwrite(chars
, sizeof(gchar
), len
, fp
) != len
) {
3458 FILE_OP_ERROR(file
, "fwrite");
3467 if (fclose(fp
) == EOF
) {
3468 FILE_OP_ERROR(file
, "fclose");
3475 static gint
compose_remove_reedit_target(Compose
*compose
)
3478 MsgInfo
*msginfo
= compose
->targetinfo
;
3480 g_return_val_if_fail(compose
->mode
== COMPOSE_REEDIT
, -1);
3481 if (!msginfo
) return -1;
3483 item
= msginfo
->folder
;
3484 g_return_val_if_fail(item
!= NULL
, -1);
3486 if (procmsg_msg_exist(msginfo
) &&
3487 (item
->stype
== F_DRAFT
|| item
->stype
== F_QUEUE
)) {
3488 if (folder_item_remove_msg(item
, msginfo
->msgnum
) < 0) {
3489 g_warning("can't remove the old message\n");
3497 void compose_remove_draft(Compose
*compose
)
3500 MsgInfo
*msginfo
= compose
->targetinfo
;
3501 drafts
= account_get_special_folder(compose
->account
, F_DRAFT
);
3503 if (procmsg_msg_exist(msginfo
)) {
3504 folder_item_remove_msg(drafts
, msginfo
->msgnum
);
3505 folder_update_item(drafts
, TRUE
);
3510 static gint
compose_queue(Compose
*compose
, gint
*msgnum
, FolderItem
**item
)
3512 return compose_queue_sub (compose
, msgnum
, item
, FALSE
);
3514 static gint
compose_queue_sub(Compose
*compose
, gint
*msgnum
, FolderItem
**item
, gboolean check_subject
)
3520 gchar buf
[BUFFSIZE
];
3522 static gboolean lock
= FALSE
;
3523 PrefsAccount
*mailac
= NULL
, *newsac
= NULL
;
3525 debug_print("queueing message...\n");
3526 g_return_val_if_fail(compose
->account
!= NULL
, -1);
3530 if (compose_check_entries(compose
, check_subject
) == FALSE
) {
3535 if (!compose
->to_list
&& !compose
->newsgroup_list
) {
3536 g_warning("can't get recipient list.");
3541 if (compose
->to_list
) {
3542 if (compose
->account
->protocol
!= A_NNTP
)
3543 mailac
= compose
->account
;
3544 else if (cur_account
&& cur_account
->protocol
!= A_NNTP
)
3545 mailac
= cur_account
;
3546 else if (!(mailac
= compose_current_mail_account())) {
3548 alertpanel_error(_("No account for sending mails available!"));
3553 if (compose
->newsgroup_list
) {
3554 if (compose
->account
->protocol
== A_NNTP
)
3555 newsac
= compose
->account
;
3556 else if (!newsac
->protocol
!= A_NNTP
) {
3558 alertpanel_error(_("No account for posting news available!"));
3563 if (prefs_common
.linewrap_at_send
)
3564 compose_wrap_line_all(compose
);
3566 /* write to temporary file */
3567 tmp2
= g_strdup_printf("%s%ctmp%d", g_get_tmp_dir(),
3568 G_DIR_SEPARATOR
, (gint
)compose
);
3570 if (compose
->redirect_filename
!= NULL
) {
3571 if (compose_redirect_write_to_file(compose
, tmp2
) < 0) {
3578 if (compose_write_to_file(compose
, tmp2
, FALSE
) < 0) {
3585 /* add queue header */
3586 tmp
= g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
3587 G_DIR_SEPARATOR
, (gint
)compose
);
3588 if ((fp
= fopen(tmp
, "wb")) == NULL
) {
3589 FILE_OP_ERROR(tmp
, "fopen");
3593 if ((src_fp
= fopen(tmp2
, "rb")) == NULL
) {
3594 FILE_OP_ERROR(tmp2
, "fopen");
3602 if (change_file_mode_rw(fp
, tmp
) < 0) {
3603 FILE_OP_ERROR(tmp
, "chmod");
3604 g_warning("can't change file mode\n");
3607 /* queueing variables */
3608 fprintf(fp
, "AF:\n");
3609 fprintf(fp
, "NF:0\n");
3610 fprintf(fp
, "PS:10\n");
3611 fprintf(fp
, "SRH:1\n");
3612 fprintf(fp
, "SFN:\n");
3613 fprintf(fp
, "DSR:\n");
3615 fprintf(fp
, "MID:<%s>\n", compose
->msgid
);
3617 fprintf(fp
, "MID:\n");
3618 fprintf(fp
, "CFG:\n");
3619 fprintf(fp
, "PT:0\n");
3620 fprintf(fp
, "S:%s\n", compose
->account
->address
);
3621 fprintf(fp
, "RQ:\n");
3623 fprintf(fp
, "SSV:%s\n", mailac
->smtp_server
);
3625 fprintf(fp
, "SSV:\n");
3627 fprintf(fp
, "NSV:%s\n", newsac
->nntp_server
);
3629 fprintf(fp
, "NSV:\n");
3630 fprintf(fp
, "SSH:\n");
3631 /* write recepient list */
3632 if (compose
->to_list
) {
3633 fprintf(fp
, "R:<%s>", (gchar
*)compose
->to_list
->data
);
3634 for (cur
= compose
->to_list
->next
; cur
!= NULL
;
3636 fprintf(fp
, ",<%s>", (gchar
*)cur
->data
);
3639 /* write newsgroup list */
3640 if (compose
->newsgroup_list
) {
3642 fprintf(fp
, "%s", (gchar
*)compose
->newsgroup_list
->data
);
3643 for (cur
= compose
->newsgroup_list
->next
; cur
!= NULL
; cur
= cur
->next
)
3644 fprintf(fp
, ",%s", (gchar
*)cur
->data
);
3647 /* Sylpheed account IDs */
3649 fprintf(fp
, "MAID:%d\n", mailac
->account_id
);
3652 fprintf(fp
, "NAID:%d\n", newsac
->account_id
);
3654 /* Save copy folder */
3655 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
3656 gchar
*savefolderid
;
3658 savefolderid
= gtk_editable_get_chars(GTK_EDITABLE(compose
->savemsg_entry
), 0, -1);
3659 fprintf(fp
, "SCF:%s\n", savefolderid
);
3660 g_free(savefolderid
);
3662 /* Message-ID of message replying to */
3663 if ((compose
->replyinfo
!= NULL
) && (compose
->replyinfo
->msgid
!= NULL
)) {
3666 folderid
= folder_item_get_identifier(compose
->replyinfo
->folder
);
3667 fprintf(fp
, "RMID:%s\x7f%d\x7f%s\n", folderid
, compose
->replyinfo
->msgnum
, compose
->replyinfo
->msgid
);
3672 while (fgets(buf
, sizeof(buf
), src_fp
) != NULL
) {
3673 if (fputs(buf
, fp
) == EOF
) {
3674 FILE_OP_ERROR(tmp
, "fputs");
3685 if (fclose(fp
) == EOF
) {
3686 FILE_OP_ERROR(tmp
, "fclose");
3694 queue
= account_get_special_folder(compose
->account
, F_QUEUE
);
3696 g_warning("can't find queue folder\n");
3701 folder_item_scan(queue
);
3702 if ((num
= folder_item_add_msg(queue
, tmp
, TRUE
)) < 0) {
3703 g_warning("can't queue the message\n");
3713 if (compose
->mode
== COMPOSE_REEDIT
) {
3714 compose_remove_reedit_target(compose
);
3715 if (compose
->targetinfo
&&
3716 compose
->targetinfo
->folder
!= queue
)
3718 (compose
->targetinfo
->folder
, TRUE
);
3721 folder_update_item(queue
, TRUE
);
3723 if ((msgnum
!= NULL
) && (item
!= NULL
)) {
3731 static void compose_write_attach(Compose
*compose
, FILE *fp
)
3734 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
3737 gchar filename
[BUFFSIZE
];
3740 for (row
= 0; (ainfo
= gtk_clist_get_row_data(clist
, row
)) != NULL
;
3742 gchar buf
[BUFFSIZE
];
3743 gchar inbuf
[B64_LINE_SIZE
], outbuf
[B64_BUFFSIZE
];
3745 if ((attach_fp
= fopen(ainfo
->file
, "rb")) == NULL
) {
3746 g_warning("Can't open file %s\n", ainfo
->file
);
3750 fprintf(fp
, "\n--%s\n", compose
->boundary
);
3752 if (!strcmp2(ainfo
->content_type
, "message/rfc822")) {
3753 fprintf(fp
, "Content-Type: %s\n", ainfo
->content_type
);
3754 fprintf(fp
, "Content-Disposition: inline\n");
3756 compose_convert_header(filename
, sizeof(filename
),
3758 fprintf(fp
, "Content-Type: %s;\n"
3760 ainfo
->content_type
, filename
);
3761 fprintf(fp
, "Content-Disposition: attachment;\n"
3762 " filename=\"%s\"\n", filename
);
3765 fprintf(fp
, "Content-Transfer-Encoding: %s\n\n",
3766 procmime_get_encoding_str(ainfo
->encoding
));
3768 switch (ainfo
->encoding
) {
3772 /* if (ainfo->encoding == ENC_7BIT) { */
3774 while (fgets(buf
, sizeof(buf
), attach_fp
) != NULL
) {
3782 while ((len
= fread(inbuf
, sizeof(gchar
),
3783 B64_LINE_SIZE
, attach_fp
))
3785 base64_encode(outbuf
, inbuf
, B64_LINE_SIZE
);
3789 if (len
> 0 && feof(attach_fp
)) {
3790 base64_encode(outbuf
, inbuf
, len
);
3796 debug_print("Tried to write attachment in unsupported encoding type\n");
3803 fprintf(fp
, "\n--%s--\n", compose
->boundary
);
3806 #define QUOTE_IF_REQUIRED(out, str) \
3808 if (*str != '"' && (strchr(str, ',') \
3809 || strchr(str, '.'))) { \
3813 len = strlen(str) + 3; \
3814 Xalloca(__tmp, len, return -1); \
3815 g_snprintf(__tmp, len, "\"%s\"", str); \
3818 Xstrdup_a(out, str, return -1); \
3822 #define PUT_RECIPIENT_HEADER(header, str) \
3824 if (*str != '\0') { \
3825 Xstrdup_a(str, str, return -1); \
3827 if (*str != '\0') { \
3828 compose->to_list = address_list_append \
3829 (compose->to_list, str); \
3830 compose_convert_header \
3831 (buf, sizeof(buf), str, strlen(header) + 2); \
3832 fprintf(fp, "%s: %s\n", header, buf); \
3837 #define IS_IN_CUSTOM_HEADER(header) \
3838 (compose->account->add_customhdr && \
3839 custom_header_find(compose->account->customhdr_list, header) != NULL)
3841 static gint
compose_write_headers_from_headerlist(Compose
*compose
,
3845 gchar buf
[BUFFSIZE
];
3846 gchar
*str
, *header_w_colon
, *trans_hdr
;
3847 gboolean first_address
;
3849 ComposeHeaderEntry
*headerentry
;
3850 gchar
* headerentryname
;
3852 if (IS_IN_CUSTOM_HEADER(header
)) {
3856 debug_print("Writing %s-header\n", header
);
3858 header_w_colon
= g_strconcat(header
, ":", NULL
);
3859 trans_hdr
= (prefs_common
.trans_hdr
? gettext(header_w_colon
) : header_w_colon
);
3861 first_address
= TRUE
;
3862 for (list
= compose
->header_list
; list
; list
= list
->next
) {
3863 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
3864 headerentryname
= gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry
->combo
)->entry
));
3866 if (!g_strcasecmp(trans_hdr
, headerentryname
)) {
3867 str
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
3868 Xstrdup_a(str
, str
, return -1);
3870 if (str
[0] != '\0') {
3871 compose_convert_header
3872 (buf
, sizeof(buf
), str
,
3873 strlen(header
) + 2);
3874 if (first_address
) {
3875 fprintf(fp
, "%s: ", header
);
3876 first_address
= FALSE
;
3880 fprintf(fp
, "%s", buf
);
3884 if (!first_address
) {
3888 g_free(header_w_colon
);
3893 static gint
compose_write_headers(Compose
*compose
, FILE *fp
,
3894 const gchar
*charset
, EncodingType encoding
,
3897 gchar buf
[BUFFSIZE
];
3901 gchar
*std_headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
3903 /* struct utsname utsbuf; */
3905 g_return_val_if_fail(fp
!= NULL
, -1);
3906 g_return_val_if_fail(charset
!= NULL
, -1);
3907 g_return_val_if_fail(compose
->account
!= NULL
, -1);
3908 g_return_val_if_fail(compose
->account
->address
!= NULL
, -1);
3910 /* Save draft infos */
3912 fprintf(fp
, "X-Sylpheed-Account-Id:%d\n", compose
->account
->account_id
);
3913 fprintf(fp
, "S:%s\n", compose
->account
->address
);
3914 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
))) {
3915 gchar
*savefolderid
;
3917 savefolderid
= gtk_editable_get_chars(GTK_EDITABLE(compose
->savemsg_entry
), 0, -1);
3918 fprintf(fp
, "SCF:%s\n", savefolderid
);
3919 g_free(savefolderid
);
3925 if (compose
->account
->add_date
) {
3926 get_rfc822_date(buf
, sizeof(buf
));
3927 fprintf(fp
, "Date: %s\n", buf
);
3931 if (!IS_IN_CUSTOM_HEADER("From")) {
3932 if (compose
->account
->name
&& *compose
->account
->name
) {
3933 compose_convert_header
3934 (buf
, sizeof(buf
), compose
->account
->name
,
3936 QUOTE_IF_REQUIRED(name
, buf
);
3937 fprintf(fp
, "From: %s <%s>\n",
3938 name
, compose
->account
->address
);
3940 fprintf(fp
, "From: %s\n", compose
->account
->address
);
3944 compose_write_headers_from_headerlist(compose
, fp
, "To");
3945 #if 0 /* NEW COMPOSE GUI */
3946 if (compose
->use_to
) {
3947 str
= gtk_entry_get_text(GTK_ENTRY(compose
->to_entry
));
3948 PUT_RECIPIENT_HEADER("To", str
);
3953 compose_write_headers_from_headerlist(compose
, fp
, "Newsgroups");
3954 #if 0 /* NEW COMPOSE GUI */
3955 if (compose
->use_newsgroups
) {
3956 str
= gtk_entry_get_text(GTK_ENTRY(compose
->newsgroups_entry
));
3958 Xstrdup_a(str
, str
, return -1);
3962 compose
->newsgroup_list
=
3963 newsgroup_list_append
3964 (compose
->newsgroup_list
, str
);
3965 compose_convert_header(buf
, sizeof(buf
), str
,
3966 strlen("Newsgroups: "));
3967 fprintf(fp
, "Newsgroups: %s\n", buf
);
3973 compose_write_headers_from_headerlist(compose
, fp
, "Cc");
3974 #if 0 /* NEW COMPOSE GUI */
3975 if (compose
->use_cc
) {
3976 str
= gtk_entry_get_text(GTK_ENTRY(compose
->cc_entry
));
3977 PUT_RECIPIENT_HEADER("Cc", str
);
3981 compose_write_headers_from_headerlist(compose
, fp
, "Bcc");
3982 #if 0 /* NEW COMPOSE GUI */
3983 if (compose
->use_bcc
) {
3984 str
= gtk_entry_get_text(GTK_ENTRY(compose
->bcc_entry
));
3985 PUT_RECIPIENT_HEADER("Bcc", str
);
3990 str
= gtk_entry_get_text(GTK_ENTRY(compose
->subject_entry
));
3991 if (*str
!= '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
3992 Xstrdup_a(str
, str
, return -1);
3995 compose_convert_header(buf
, sizeof(buf
), str
,
3996 strlen("Subject: "));
3997 fprintf(fp
, "Subject: %s\n", buf
);
4002 if (compose
->account
->gen_msgid
) {
4003 compose_generate_msgid(compose
, buf
, sizeof(buf
));
4004 fprintf(fp
, "Message-Id: <%s>\n", buf
);
4005 compose
->msgid
= g_strdup(buf
);
4009 if (compose
->inreplyto
&& compose
->to_list
)
4010 fprintf(fp
, "In-Reply-To: <%s>\n", compose
->inreplyto
);
4013 if (compose
->references
)
4014 fprintf(fp
, "References: %s\n", compose
->references
);
4017 compose_write_headers_from_headerlist(compose
, fp
, "Followup-To");
4018 #if 0 /* NEW COMPOSE GUI */
4019 if (compose
->use_followupto
&& !IS_IN_CUSTOM_HEADER("Followup-To")) {
4020 str
= gtk_entry_get_text(GTK_ENTRY(compose
->followup_entry
));
4022 Xstrdup_a(str
, str
, return -1);
4026 compose_convert_header(buf
, sizeof(buf
), str
,
4027 strlen("Followup-To: "));
4028 fprintf(fp
, "Followup-To: %s\n", buf
);
4034 compose_write_headers_from_headerlist(compose
, fp
, "Reply-To");
4035 #if 0 /* NEW COMPOSE GUI */
4036 if (compose
->use_replyto
&& !IS_IN_CUSTOM_HEADER("Reply-To")) {
4037 str
= gtk_entry_get_text(GTK_ENTRY(compose
->reply_entry
));
4039 Xstrdup_a(str
, str
, return -1);
4042 compose_convert_header(buf
, sizeof(buf
), str
,
4043 strlen("Reply-To: "));
4044 fprintf(fp
, "Reply-To: %s\n", buf
);
4050 if (compose
->account
->organization
&&
4051 !IS_IN_CUSTOM_HEADER("Organization")) {
4052 compose_convert_header(buf
, sizeof(buf
),
4053 compose
->account
->organization
,
4054 strlen("Organization: "));
4055 fprintf(fp
, "Organization: %s\n", buf
);
4058 /* Program version and system info */
4059 /* uname(&utsbuf); */
4060 if (g_slist_length(compose
->to_list
) && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
4061 fprintf(fp
, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
4063 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
4065 /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
4067 if (g_slist_length(compose
->newsgroup_list
) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
4068 fprintf(fp
, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
4070 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
4072 /* utsbuf.sysname, utsbuf.release, utsbuf.machine); */
4075 /* custom headers */
4076 if (compose
->account
->add_customhdr
) {
4079 for (cur
= compose
->account
->customhdr_list
; cur
!= NULL
;
4081 CustomHeader
*chdr
= (CustomHeader
*)cur
->data
;
4083 if (strcasecmp(chdr
->name
, "Date") != 0 &&
4084 strcasecmp(chdr
->name
, "From") != 0 &&
4085 strcasecmp(chdr
->name
, "To") != 0 &&
4086 /* strcasecmp(chdr->name, "Sender") != 0 && */
4087 strcasecmp(chdr
->name
, "Message-Id") != 0 &&
4088 strcasecmp(chdr
->name
, "In-Reply-To") != 0 &&
4089 strcasecmp(chdr
->name
, "References") != 0 &&
4090 strcasecmp(chdr
->name
, "Mime-Version") != 0 &&
4091 strcasecmp(chdr
->name
, "Content-Type") != 0 &&
4092 strcasecmp(chdr
->name
, "Content-Transfer-Encoding")
4094 compose_convert_header
4096 chdr
->value
? chdr
->value
: "",
4097 strlen(chdr
->name
) + 2);
4098 fprintf(fp
, "%s: %s\n", chdr
->name
, buf
);
4104 fprintf(fp
, "Mime-Version: 1.0\n");
4105 if (compose_use_attach(compose
)) {
4106 get_rfc822_date(buf
, sizeof(buf
));
4107 subst_char(buf
, ' ', '_');
4108 subst_char(buf
, ',', '_');
4109 compose
->boundary
= g_strdup_printf("Multipart_%s_%08x",
4110 buf
, (guint
)compose
);
4112 "Content-Type: multipart/mixed;\n"
4113 " boundary=\"%s\"\n", compose
->boundary
);
4115 fprintf(fp
, "Content-Type: text/plain; charset=%s\n", charset
);
4116 fprintf(fp
, "Content-Transfer-Encoding: %s\n",
4117 procmime_get_encoding_str(encoding
));
4121 switch (compose
->priority
) {
4122 case PRIORITY_HIGHEST
: fprintf(fp
, "Importance: high\n"
4123 "X-Priority: 1 (Highest)\n");
4125 case PRIORITY_HIGH
: fprintf(fp
, "Importance: high\n"
4126 "X-Priority: 2 (High)\n");
4128 case PRIORITY_NORMAL
: break;
4129 case PRIORITY_LOW
: fprintf(fp
, "Importance: low\n"
4130 "X-Priority: 4 (Low)\n");
4132 case PRIORITY_LOWEST
: fprintf(fp
, "Importance: low\n"
4133 "X-Priority: 5 (Lowest)\n");
4135 default: debug_print("compose: priority unknown : %d\n",
4139 /* Request Return Receipt */
4140 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
4141 if (compose
->return_receipt
) {
4142 if (compose
->account
->name
4143 && *compose
->account
->name
) {
4144 compose_convert_header(buf
, sizeof(buf
), compose
->account
->name
, strlen("Disposition-Notification-To: "));
4145 fprintf(fp
, "Disposition-Notification-To: %s <%s>\n", buf
, compose
->account
->address
);
4147 fprintf(fp
, "Disposition-Notification-To: %s\n", compose
->account
->address
);
4151 /* get special headers */
4152 for (list
= compose
->header_list
; list
; list
= list
->next
) {
4153 ComposeHeaderEntry
*headerentry
;
4156 gchar
*headername_wcolon
;
4157 gchar
*headername_trans
;
4160 gboolean standard_header
= FALSE
;
4162 headerentry
= ((ComposeHeaderEntry
*)list
->data
);
4164 tmp
= g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry
->combo
)->entry
)));
4165 if (!strstr(tmp
, ":")) {
4166 headername_wcolon
= g_strconcat(tmp
, ":", NULL
);
4167 headername
= g_strdup(tmp
);
4169 headername_wcolon
= g_strdup(tmp
);
4170 headername
= g_strdup(strtok(tmp
, ":"));
4174 headervalue
= gtk_entry_get_text(GTK_ENTRY(headerentry
->entry
));
4175 string
= std_headers
;
4176 while (*string
!= NULL
) {
4177 headername_trans
= prefs_common
.trans_hdr
? gettext(*string
) : *string
;
4178 if (!strcmp(headername_trans
,headername_wcolon
))
4179 standard_header
= TRUE
;
4182 if (!standard_header
&& !IS_IN_CUSTOM_HEADER(headername
))
4183 fprintf(fp
, "%s %s\n", headername_wcolon
, headervalue
);
4186 g_free(headername_wcolon
);
4190 /* separator between header and body */
4196 #undef IS_IN_CUSTOM_HEADER
4198 static void compose_convert_header(gchar
*dest
, gint len
, gchar
*src
,
4201 g_return_if_fail(src
!= NULL
);
4202 g_return_if_fail(dest
!= NULL
);
4204 if (len
< 1) return;
4208 conv_encode_header(dest
, len
, src
, header_len
);
4211 static void compose_generate_msgid(Compose
*compose
, gchar
*buf
, gint len
)
4220 if (compose
->account
&& compose
->account
->address
&&
4221 *compose
->account
->address
) {
4222 if (strchr(compose
->account
->address
, '@'))
4223 addr
= g_strdup(compose
->account
->address
);
4225 addr
= g_strconcat(compose
->account
->address
, "@",
4226 get_domain_name(), NULL
);
4228 addr
= g_strconcat(g_get_user_name(), "@", get_domain_name(),
4231 g_snprintf(buf
, len
, "%04d%02d%02d%02d%02d%02d.%08x.%s",
4232 lt
->tm_year
+ 1900, lt
->tm_mon
+ 1,
4233 lt
->tm_mday
, lt
->tm_hour
,
4234 lt
->tm_min
, lt
->tm_sec
,
4235 (guint
)random(), addr
);
4237 debug_print("generated Message-ID: %s\n", buf
);
4242 static void compose_create_header_entry(Compose
*compose
)
4244 gchar
*headers
[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL
};
4248 GList
*combo_list
= NULL
;
4249 gchar
**string
, *header
= NULL
;
4250 ComposeHeaderEntry
*headerentry
;
4251 gboolean standard_header
= FALSE
;
4253 headerentry
= g_new0(ComposeHeaderEntry
, 1);
4256 combo
= gtk_combo_new();
4258 while(*string
!= NULL
) {
4259 combo_list
= g_list_append(combo_list
, (prefs_common
.trans_hdr
? gettext(*string
) : *string
));
4262 gtk_combo_set_popdown_strings(GTK_COMBO(combo
), combo_list
);
4263 g_list_free(combo_list
);
4264 gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(combo
)->entry
), TRUE
);
4265 gtk_widget_show(combo
);
4266 gtk_table_attach(GTK_TABLE(compose
->header_table
), combo
, 0, 1, compose
->header_nextrow
, compose
->header_nextrow
+1, GTK_SHRINK
, GTK_FILL
, 0, 0);
4267 if (compose
->header_last
) {
4268 gchar
*last_header_entry
= gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose
->header_last
->combo
)->entry
));
4270 while (*string
!= NULL
) {
4271 if (!strcmp(*string
, last_header_entry
))
4272 standard_header
= TRUE
;
4275 if (standard_header
)
4276 header
= gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose
->header_last
->combo
)->entry
));
4278 if (!compose
->header_last
|| !standard_header
) {
4279 switch(compose
->account
->protocol
) {
4281 header
= prefs_common
.trans_hdr
? _("Newsgroups:") : "Newsgroups:";
4284 header
= prefs_common
.trans_hdr
? _("To:") : "To:";
4289 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo
)->entry
), header
);
4292 entry
= gtk_entry_new();
4293 gtk_widget_show(entry
);
4294 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);
4296 gtk_signal_connect(GTK_OBJECT(entry
), "key-press-event", GTK_SIGNAL_FUNC(compose_headerentry_key_press_event_cb
), headerentry
);
4297 gtk_signal_connect(GTK_OBJECT(entry
), "changed", GTK_SIGNAL_FUNC(compose_headerentry_changed_cb
), headerentry
);
4298 gtk_signal_connect(GTK_OBJECT(entry
), "activate", GTK_SIGNAL_FUNC(text_activated
), compose
);
4300 address_completion_register_entry(GTK_ENTRY(entry
));
4302 headerentry
->compose
= compose
;
4303 headerentry
->combo
= combo
;
4304 headerentry
->entry
= entry
;
4305 headerentry
->headernum
= compose
->header_nextrow
;
4307 compose
->header_nextrow
++;
4308 compose
->header_last
= headerentry
;
4311 static void compose_add_header_entry(Compose
*compose
, gchar
*header
, gchar
*text
)
4313 ComposeHeaderEntry
*last_header
;
4315 last_header
= compose
->header_last
;
4317 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(last_header
->combo
)->entry
), header
);
4318 gtk_entry_set_text(GTK_ENTRY(last_header
->entry
), text
);
4321 static GtkWidget
*compose_create_header(Compose
*compose
)
4325 GtkWidget
*from_optmenu_hbox
;
4326 #if 0 /* NEW COMPOSE GUI */
4327 GtkWidget
*to_entry
;
4329 GtkWidget
*newsgroups_entry
;
4330 GtkWidget
*newsgroups_hbox
;
4332 GtkWidget
*header_scrolledwin
;
4333 GtkWidget
*header_table
;
4334 #if 0 /* NEW COMPOSE GUI */
4335 GtkWidget
*cc_entry
;
4337 GtkWidget
*bcc_entry
;
4338 GtkWidget
*bcc_hbox
;
4339 GtkWidget
*reply_entry
;
4340 GtkWidget
*reply_hbox
;
4341 GtkWidget
*followup_entry
;
4342 GtkWidget
*followup_hbox
;
4347 /* header labels and entries */
4348 header_scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
4349 gtk_widget_show(header_scrolledwin
);
4350 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
4352 header_table
= gtk_table_new(2, 2, FALSE
);
4353 gtk_widget_show(header_table
);
4354 gtk_container_set_border_width(GTK_CONTAINER(header_table
), 2);
4355 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin
), header_table
);
4356 gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin
)->child
), GTK_SHADOW_ETCHED_IN
);
4359 /* option menu for selecting accounts */
4360 hbox
= gtk_hbox_new(FALSE
, 0);
4361 label
= gtk_label_new(prefs_common
.trans_hdr
? _("From:") : "From:");
4362 gtk_box_pack_end(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
4363 gtk_table_attach(GTK_TABLE(header_table
), hbox
, 0, 1, count
, count
+ 1,
4365 from_optmenu_hbox
= compose_account_option_menu_create(compose
);
4366 gtk_table_attach(GTK_TABLE(header_table
), from_optmenu_hbox
,
4367 1, 2, count
, count
+ 1, GTK_EXPAND
| GTK_FILL
, GTK_SHRINK
, 0, 0);
4368 #if 0 /* NEW COMPOSE GUI */
4369 gtk_table_set_row_spacing(GTK_TABLE(table
), 0, 4);
4373 compose
->header_table
= header_table
;
4374 compose
->header_list
= NULL
;
4375 compose
->header_nextrow
= count
;
4377 compose_create_header_entry(compose
);
4379 #if 0 /* NEW COMPOSE GUI */
4380 compose_add_entry_field(table
, &to_hbox
, &to_entry
, &count
,
4382 gtk_table_set_row_spacing(GTK_TABLE(table
), 0, 4);
4383 compose_add_entry_field(table
, &newsgroups_hbox
, &newsgroups_entry
,
4384 &count
, "Newsgroups:", FALSE
);
4385 gtk_table_set_row_spacing(GTK_TABLE(table
), 1, 4);
4387 gtk_table_set_row_spacing(GTK_TABLE(table
), 2, 4);
4389 compose_add_entry_field(table
, &cc_hbox
, &cc_entry
, &count
,
4391 gtk_table_set_row_spacing(GTK_TABLE(table
), 3, 4);
4392 compose_add_entry_field(table
, &bcc_hbox
, &bcc_entry
, &count
,
4394 gtk_table_set_row_spacing(GTK_TABLE(table
), 4, 4);
4395 compose_add_entry_field(table
, &reply_hbox
, &reply_entry
, &count
,
4397 gtk_table_set_row_spacing(GTK_TABLE(table
), 5, 4);
4398 compose_add_entry_field(table
, &followup_hbox
, &followup_entry
, &count
,
4399 "Followup-To:", FALSE
);
4400 gtk_table_set_row_spacing(GTK_TABLE(table
), 6, 4);
4402 gtk_table_set_col_spacings(GTK_TABLE(table
), 4);
4404 gtk_signal_connect(GTK_OBJECT(to_entry
), "activate",
4405 GTK_SIGNAL_FUNC(to_activated
), compose
);
4406 gtk_signal_connect(GTK_OBJECT(newsgroups_entry
), "activate",
4407 GTK_SIGNAL_FUNC(newsgroups_activated
), compose
);
4408 gtk_signal_connect(GTK_OBJECT(subject_entry
), "activate",
4409 GTK_SIGNAL_FUNC(subject_activated
), compose
);
4410 gtk_signal_connect(GTK_OBJECT(cc_entry
), "activate",
4411 GTK_SIGNAL_FUNC(cc_activated
), compose
);
4412 gtk_signal_connect(GTK_OBJECT(bcc_entry
), "activate",
4413 GTK_SIGNAL_FUNC(bcc_activated
), compose
);
4414 gtk_signal_connect(GTK_OBJECT(reply_entry
), "activate",
4415 GTK_SIGNAL_FUNC(replyto_activated
), compose
);
4416 gtk_signal_connect(GTK_OBJECT(followup_entry
), "activate",
4417 GTK_SIGNAL_FUNC(followupto_activated
), compose
);
4419 gtk_signal_connect(GTK_OBJECT(subject_entry
), "grab_focus",
4420 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4421 gtk_signal_connect(GTK_OBJECT(to_entry
), "grab_focus",
4422 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4423 gtk_signal_connect(GTK_OBJECT(newsgroups_entry
), "grab_focus",
4424 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4425 gtk_signal_connect(GTK_OBJECT(cc_entry
), "grab_focus",
4426 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4427 gtk_signal_connect(GTK_OBJECT(bcc_entry
), "grab_focus",
4428 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4429 gtk_signal_connect(GTK_OBJECT(reply_entry
), "grab_focus",
4430 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4431 gtk_signal_connect(GTK_OBJECT(followup_entry
), "grab_focus",
4432 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4435 compose
->table
= NULL
;
4436 #if 0 /* NEW COMPOSE GUI */
4437 compose
->table
= table
;
4438 compose
->to_hbox
= to_hbox
;
4439 compose
->to_entry
= to_entry
;
4440 compose
->newsgroups_hbox
= newsgroups_hbox
;
4441 compose
->newsgroups_entry
= newsgroups_entry
;
4443 #if 0 /* NEW COMPOSE GUI */
4444 compose
->cc_hbox
= cc_hbox
;
4445 compose
->cc_entry
= cc_entry
;
4446 compose
->bcc_hbox
= bcc_hbox
;
4447 compose
->bcc_entry
= bcc_entry
;
4448 compose
->reply_hbox
= reply_hbox
;
4449 compose
->reply_entry
= reply_entry
;
4450 compose
->followup_hbox
= followup_hbox
;
4451 compose
->followup_entry
= followup_entry
;
4454 return header_scrolledwin
;
4457 GtkWidget
*compose_create_attach(Compose
*compose
)
4459 gchar
*titles
[N_ATTACH_COLS
];
4462 GtkWidget
*attach_scrwin
;
4463 GtkWidget
*attach_clist
;
4465 titles
[COL_MIMETYPE
] = _("MIME type");
4466 titles
[COL_SIZE
] = _("Size");
4467 titles
[COL_NAME
] = _("Name");
4469 /* attachment list */
4470 attach_scrwin
= gtk_scrolled_window_new(NULL
, NULL
);
4471 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin
),
4472 GTK_POLICY_AUTOMATIC
,
4474 gtk_widget_set_usize(attach_scrwin
, -1, 80);
4476 attach_clist
= gtk_clist_new_with_titles(N_ATTACH_COLS
, titles
);
4477 gtk_clist_set_column_justification(GTK_CLIST(attach_clist
), COL_SIZE
,
4479 gtk_clist_set_column_width(GTK_CLIST(attach_clist
), COL_MIMETYPE
, 240);
4480 gtk_clist_set_column_width(GTK_CLIST(attach_clist
), COL_SIZE
, 64);
4481 gtk_clist_set_selection_mode(GTK_CLIST(attach_clist
),
4482 GTK_SELECTION_EXTENDED
);
4483 for (i
= 0; i
< N_ATTACH_COLS
; i
++)
4484 GTK_WIDGET_UNSET_FLAGS
4485 (GTK_CLIST(attach_clist
)->column
[i
].button
,
4487 gtk_container_add(GTK_CONTAINER(attach_scrwin
), attach_clist
);
4489 gtk_signal_connect(GTK_OBJECT(attach_clist
), "select_row",
4490 GTK_SIGNAL_FUNC(attach_selected
), compose
);
4491 gtk_signal_connect(GTK_OBJECT(attach_clist
), "button_press_event",
4492 GTK_SIGNAL_FUNC(attach_button_pressed
), compose
);
4493 gtk_signal_connect(GTK_OBJECT(attach_clist
), "key_press_event",
4494 GTK_SIGNAL_FUNC(attach_key_pressed
), compose
);
4497 gtk_drag_dest_set(attach_clist
,
4498 GTK_DEST_DEFAULT_ALL
, compose_mime_types
, 1,
4499 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
4500 gtk_signal_connect(GTK_OBJECT(attach_clist
), "drag_data_received",
4501 GTK_SIGNAL_FUNC(compose_attach_drag_received_cb
),
4504 compose
->attach_scrwin
= attach_scrwin
;
4505 compose
->attach_clist
= attach_clist
;
4507 return attach_scrwin
;
4510 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
);
4511 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
);
4513 static GtkWidget
*compose_create_others(Compose
*compose
)
4516 GtkWidget
*savemsg_checkbtn
;
4517 GtkWidget
*savemsg_entry
;
4518 GtkWidget
*savemsg_select
;
4521 gchar
*folderidentifier
;
4523 /* Table for settings */
4524 table
= gtk_table_new(3, 1, FALSE
);
4525 gtk_widget_show(table
);
4526 gtk_table_set_row_spacings(GTK_TABLE(table
), VSPACING_NARROW
);
4529 /* Save Message to folder */
4530 savemsg_checkbtn
= gtk_check_button_new_with_label(_("Save Message to "));
4531 gtk_widget_show(savemsg_checkbtn
);
4532 gtk_table_attach(GTK_TABLE(table
), savemsg_checkbtn
, 0, 1, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
4533 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
4534 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn
), prefs_common
.savemsg
);
4536 gtk_signal_connect(GTK_OBJECT(savemsg_checkbtn
), "toggled",
4537 GTK_SIGNAL_FUNC(compose_savemsg_checkbtn_cb
), compose
);
4539 savemsg_entry
= gtk_entry_new();
4540 gtk_widget_show(savemsg_entry
);
4541 gtk_table_attach_defaults(GTK_TABLE(table
), savemsg_entry
, 1, 2, rowcount
, rowcount
+ 1);
4542 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry
), prefs_common
.savemsg
);
4543 if (account_get_special_folder(compose
->account
, F_OUTBOX
)) {
4544 folderidentifier
= folder_item_get_identifier(account_get_special_folder
4545 (compose
->account
, F_OUTBOX
));
4546 gtk_entry_set_text(GTK_ENTRY(savemsg_entry
), folderidentifier
);
4547 g_free(folderidentifier
);
4550 savemsg_select
= gtk_button_new_with_label (_("Select ..."));
4551 gtk_widget_show (savemsg_select
);
4552 gtk_table_attach(GTK_TABLE(table
), savemsg_select
, 2, 3, rowcount
, rowcount
+ 1, GTK_SHRINK
| GTK_FILL
, GTK_SHRINK
, 0, 0);
4553 gtk_signal_connect (GTK_OBJECT (savemsg_select
), "clicked",
4554 GTK_SIGNAL_FUNC (compose_savemsg_select_cb
),
4559 compose
->savemsg_checkbtn
= savemsg_checkbtn
;
4560 compose
->savemsg_entry
= savemsg_entry
;
4565 static void compose_savemsg_checkbtn_cb(GtkWidget
*widget
, Compose
*compose
)
4567 gtk_editable_set_editable(GTK_EDITABLE(compose
->savemsg_entry
),
4568 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose
->savemsg_checkbtn
)));
4571 static void compose_savemsg_select_cb(GtkWidget
*widget
, Compose
*compose
)
4576 dest
= foldersel_folder_sel(NULL
, FOLDER_SEL_COPY
, NULL
);
4579 path
= folder_item_get_identifier(dest
);
4581 gtk_entry_set_text(GTK_ENTRY(compose
->savemsg_entry
), path
);
4585 static Compose
*compose_create(PrefsAccount
*account
, ComposeMode mode
)
4591 GtkWidget
*handlebox
;
4593 GtkWidget
*notebook
;
4598 GtkWidget
*subject_hbox
;
4599 GtkWidget
*subject_frame
;
4600 GtkWidget
*subject_entry
;
4604 GtkWidget
*edit_vbox
;
4605 GtkWidget
*ruler_hbox
;
4607 GtkWidget
*scrolledwin
;
4610 UndoMain
*undostruct
;
4612 gchar
*titles
[N_ATTACH_COLS
];
4613 guint n_menu_entries
;
4614 GtkStyle
*style
, *new_style
;
4617 gboolean success
[1];
4618 GtkWidget
*popupmenu
;
4619 GtkItemFactory
*popupfactory
;
4620 GtkItemFactory
*ifactory
;
4621 GtkWidget
*tmpl_menu
;
4625 GtkAspell
* gtkaspell
= NULL
;
4628 static GdkGeometry geometry
;
4630 g_return_val_if_fail(account
!= NULL
, NULL
);
4632 debug_print("Creating compose window...\n");
4633 compose
= g_new0(Compose
, 1);
4635 titles
[COL_MIMETYPE
] = _("MIME type");
4636 titles
[COL_SIZE
] = _("Size");
4637 titles
[COL_NAME
] = _("Name");
4639 compose
->account
= account
;
4641 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
4642 gtk_window_set_policy(GTK_WINDOW(window
), TRUE
, TRUE
, FALSE
);
4643 gtk_widget_set_usize(window
, -1, prefs_common
.compose_height
);
4644 gtk_window_set_wmclass(GTK_WINDOW(window
), "compose window", "Sylpheed");
4646 if (!geometry
.max_width
) {
4647 geometry
.max_width
= gdk_screen_width();
4648 geometry
.max_height
= gdk_screen_height();
4650 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
,
4651 &geometry
, GDK_HINT_MAX_SIZE
);
4653 gtk_signal_connect(GTK_OBJECT(window
), "delete_event",
4654 GTK_SIGNAL_FUNC(compose_delete_cb
), compose
);
4655 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
4656 GTK_SIGNAL_FUNC(compose_destroy_cb
), compose
);
4657 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
4658 gtk_widget_realize(window
);
4660 gtkut_widget_set_composer_icon(window
);
4662 vbox
= gtk_vbox_new(FALSE
, 0);
4663 gtk_container_add(GTK_CONTAINER(window
), vbox
);
4665 n_menu_entries
= sizeof(compose_entries
) / sizeof(compose_entries
[0]);
4666 menubar
= menubar_create(window
, compose_entries
,
4667 n_menu_entries
, "<Compose>", compose
);
4668 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
4670 handlebox
= gtk_handle_box_new();
4671 gtk_box_pack_start(GTK_BOX(vbox
), handlebox
, FALSE
, FALSE
, 0);
4673 compose
->toolbar
= toolbar_create(TOOLBAR_COMPOSE
, handlebox
,
4676 vbox2
= gtk_vbox_new(FALSE
, 2);
4677 gtk_box_pack_start(GTK_BOX(vbox
), vbox2
, TRUE
, TRUE
, 0);
4678 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), BORDER_WIDTH
);
4681 notebook
= gtk_notebook_new();
4682 gtk_widget_set_usize(notebook
, -1, 130);
4683 gtk_widget_show(notebook
);
4685 /* header labels and entries */
4686 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), compose_create_header(compose
), gtk_label_new(_("Header")));
4687 /* attachment list */
4688 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), compose_create_attach(compose
), gtk_label_new(_("Attachments")));
4690 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), compose_create_others(compose
), gtk_label_new(_("Others")));
4693 subject_hbox
= gtk_hbox_new(FALSE
, 0);
4694 gtk_widget_show(subject_hbox
);
4696 subject_frame
= gtk_frame_new(NULL
);
4697 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame
), GTK_SHADOW_OUT
);
4698 gtk_box_pack_start(GTK_BOX(subject_hbox
), subject_frame
, TRUE
, TRUE
, BORDER_WIDTH
+1);
4699 gtk_widget_show(subject_frame
);
4701 subject
= gtk_hbox_new(FALSE
, 0);
4702 gtk_container_set_border_width(GTK_CONTAINER(subject
), BORDER_WIDTH
);
4703 gtk_widget_show(subject
);
4705 label
= gtk_label_new(_("Subject:"));
4706 gtk_box_pack_start(GTK_BOX(subject
), label
, FALSE
, FALSE
, 4);
4707 gtk_widget_show(label
);
4709 subject_entry
= gtk_entry_new();
4710 gtk_box_pack_start(GTK_BOX(subject
), subject_entry
, TRUE
, TRUE
, 2);
4711 gtk_signal_connect(GTK_OBJECT(subject_entry
), "activate", GTK_SIGNAL_FUNC(text_activated
), compose
);
4712 gtk_widget_show(subject_entry
);
4713 compose
->subject_entry
= subject_entry
;
4714 gtk_container_add(GTK_CONTAINER(subject_frame
), subject
);
4716 edit_vbox
= gtk_vbox_new(FALSE
, 0);
4717 #if 0 /* NEW COMPOSE GUI */
4718 gtk_box_pack_start(GTK_BOX(vbox2
), edit_vbox
, TRUE
, TRUE
, 0);
4721 gtk_box_pack_start(GTK_BOX(edit_vbox
), subject_hbox
, FALSE
, FALSE
, 0);
4724 ruler_hbox
= gtk_hbox_new(FALSE
, 0);
4725 gtk_box_pack_start(GTK_BOX(edit_vbox
), ruler_hbox
, FALSE
, FALSE
, 0);
4727 ruler
= gtk_shruler_new();
4728 gtk_ruler_set_range(GTK_RULER(ruler
), 0.0, 100.0, 1.0, 100.0);
4729 gtk_box_pack_start(GTK_BOX(ruler_hbox
), ruler
, TRUE
, TRUE
,
4731 gtk_widget_set_usize(ruler_hbox
, 1, -1);
4734 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
4735 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
4736 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
4737 gtk_box_pack_start(GTK_BOX(edit_vbox
), scrolledwin
, TRUE
, TRUE
, 0);
4738 gtk_widget_set_usize(scrolledwin
, prefs_common
.compose_width
, -1);
4740 text
= gtk_stext_new(gtk_scrolled_window_get_hadjustment
4741 (GTK_SCROLLED_WINDOW(scrolledwin
)),
4742 gtk_scrolled_window_get_vadjustment
4743 (GTK_SCROLLED_WINDOW(scrolledwin
)));
4744 GTK_STEXT(text
)->default_tab_width
= 8;
4745 gtk_stext_set_editable(GTK_STEXT(text
), TRUE
);
4747 if (prefs_common
.block_cursor
) {
4748 GTK_STEXT(text
)->cursor_type
= GTK_STEXT_CURSOR_BLOCK
;
4751 if (prefs_common
.smart_wrapping
) {
4752 gtk_stext_set_word_wrap(GTK_STEXT(text
), TRUE
);
4753 gtk_stext_set_wrap_rmargin(GTK_STEXT(text
), prefs_common
.linewrap_len
);
4756 gtk_container_add(GTK_CONTAINER(scrolledwin
), text
);
4758 gtk_signal_connect(GTK_OBJECT(text
), "changed",
4759 GTK_SIGNAL_FUNC(compose_changed_cb
), compose
);
4760 gtk_signal_connect(GTK_OBJECT(text
), "grab_focus",
4761 GTK_SIGNAL_FUNC(compose_grab_focus_cb
), compose
);
4762 gtk_signal_connect(GTK_OBJECT(text
), "activate",
4763 GTK_SIGNAL_FUNC(text_activated
), compose
);
4764 gtk_signal_connect(GTK_OBJECT(text
), "insert_text",
4765 GTK_SIGNAL_FUNC(text_inserted
), compose
);
4766 gtk_signal_connect_after(GTK_OBJECT(text
), "button_press_event",
4767 GTK_SIGNAL_FUNC(compose_button_press_cb
),
4770 gtk_signal_connect_after(GTK_OBJECT(text
), "key_press_event",
4771 GTK_SIGNAL_FUNC(compose_key_press_cb
),
4774 gtk_signal_connect_after(GTK_OBJECT(text
), "size_allocate",
4775 GTK_SIGNAL_FUNC(compose_edit_size_alloc
),
4779 gtk_drag_dest_set(text
, GTK_DEST_DEFAULT_ALL
, compose_mime_types
, 1,
4780 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
4781 gtk_signal_connect(GTK_OBJECT(text
), "drag_data_received",
4782 GTK_SIGNAL_FUNC(compose_insert_drag_received_cb
),
4784 gtk_widget_show_all(vbox
);
4786 /* pane between attach clist and text */
4787 paned
= gtk_vpaned_new();
4788 gtk_paned_set_gutter_size(GTK_PANED(paned
), 12);
4789 gtk_paned_set_handle_size(GTK_PANED(paned
), 12);
4790 gtk_container_add(GTK_CONTAINER(vbox2
), paned
);
4791 gtk_paned_add1(GTK_PANED(paned
), notebook
);
4792 gtk_paned_add2(GTK_PANED(paned
), edit_vbox
);
4793 gtk_widget_show_all(paned
);
4795 style
= gtk_widget_get_style(text
);
4797 /* workaround for the slow down of GtkSText when using Pixmap theme */
4798 if (style
->engine
) {
4799 GtkThemeEngine
*engine
;
4801 engine
= style
->engine
;
4802 style
->engine
= NULL
;
4803 new_style
= gtk_style_copy(style
);
4804 style
->engine
= engine
;
4806 new_style
= gtk_style_copy(style
);
4808 if (prefs_common
.textfont
) {
4811 if ((font
= gtkut_font_load(prefs_common
.textfont
)) != NULL
) {
4812 gdk_font_unref(new_style
->font
);
4813 new_style
->font
= font
;
4817 gtk_widget_set_style(text
, new_style
);
4819 color
[0] = quote_color
;
4820 cmap
= gdk_window_get_colormap(window
->window
);
4821 gdk_colormap_alloc_colors(cmap
, color
, 1, FALSE
, TRUE
, success
);
4822 if (success
[0] == FALSE
) {
4823 g_warning("Compose: color allocation failed.\n");
4824 style
= gtk_widget_get_style(text
);
4825 quote_color
= style
->black
;
4828 n_entries
= sizeof(compose_popup_entries
) /
4829 sizeof(compose_popup_entries
[0]);
4830 popupmenu
= menu_create_items(compose_popup_entries
, n_entries
,
4831 "<Compose>", &popupfactory
,
4834 ifactory
= gtk_item_factory_from_widget(menubar
);
4835 menu_set_sensitive(ifactory
, "/Edit/Undo", FALSE
);
4836 menu_set_sensitive(ifactory
, "/Edit/Redo", FALSE
);
4838 tmpl_menu
= gtk_item_factory_get_item(ifactory
, "/Tools/Template");
4839 #if 0 /* NEW COMPOSE GUI */
4840 gtk_widget_hide(bcc_hbox
);
4841 gtk_widget_hide(bcc_entry
);
4842 gtk_widget_hide(reply_hbox
);
4843 gtk_widget_hide(reply_entry
);
4844 gtk_widget_hide(followup_hbox
);
4845 gtk_widget_hide(followup_entry
);
4846 gtk_widget_hide(ruler_hbox
);
4847 gtk_table_set_row_spacing(GTK_TABLE(table
), 4, 0);
4848 gtk_table_set_row_spacing(GTK_TABLE(table
), 5, 0);
4849 gtk_table_set_row_spacing(GTK_TABLE(table
), 6, 0);
4851 if (account
->protocol
== A_NNTP
) {
4852 gtk_widget_hide(to_hbox
);
4853 gtk_widget_hide(to_entry
);
4854 gtk_widget_hide(cc_hbox
);
4855 gtk_widget_hide(cc_entry
);
4856 gtk_table_set_row_spacing(GTK_TABLE(table
), 1, 0);
4857 gtk_table_set_row_spacing(GTK_TABLE(table
), 3, 0);
4859 gtk_widget_hide(newsgroups_hbox
);
4860 gtk_widget_hide(newsgroups_entry
);
4861 gtk_table_set_row_spacing(GTK_TABLE(table
), 2, 0);
4865 update_compose_actions_menu(ifactory
, "/Tools/Actions", compose
);
4868 undostruct
= undo_init(text
);
4869 undo_set_change_state_func(undostruct
, &compose_undo_state_changed
,
4872 address_completion_start(window
);
4874 compose
->window
= window
;
4875 compose
->vbox
= vbox
;
4876 compose
->menubar
= menubar
;
4877 compose
->handlebox
= handlebox
;
4879 compose
->vbox2
= vbox2
;
4881 compose
->paned
= paned
;
4883 compose
->edit_vbox
= edit_vbox
;
4884 compose
->ruler_hbox
= ruler_hbox
;
4885 compose
->ruler
= ruler
;
4886 compose
->scrolledwin
= scrolledwin
;
4887 compose
->text
= text
;
4889 compose
->focused_editable
= NULL
;
4891 compose
->popupmenu
= popupmenu
;
4892 compose
->popupfactory
= popupfactory
;
4894 compose
->tmpl_menu
= tmpl_menu
;
4896 compose
->mode
= mode
;
4898 compose
->targetinfo
= NULL
;
4899 compose
->replyinfo
= NULL
;
4901 compose
->replyto
= NULL
;
4903 compose
->bcc
= NULL
;
4904 compose
->followup_to
= NULL
;
4906 compose
->ml_post
= NULL
;
4908 compose
->inreplyto
= NULL
;
4909 compose
->references
= NULL
;
4910 compose
->msgid
= NULL
;
4911 compose
->boundary
= NULL
;
4914 compose
->use_signing
= FALSE
;
4915 compose
->use_encryption
= FALSE
;
4916 #endif /* USE_GPGME */
4918 compose
->modified
= FALSE
;
4920 compose
->return_receipt
= FALSE
;
4921 compose
->paste_as_quotation
= FALSE
;
4923 compose
->to_list
= NULL
;
4924 compose
->newsgroup_list
= NULL
;
4926 compose
->exteditor_file
= NULL
;
4927 compose
->exteditor_pid
= -1;
4928 compose
->exteditor_readdes
= -1;
4929 compose
->exteditor_tag
= -1;
4931 compose
->redirect_filename
= NULL
;
4932 compose
->undostruct
= undostruct
;
4934 menu_set_sensitive(ifactory
, "/Spelling", FALSE
);
4935 if (mode
!= COMPOSE_REDIRECT
) {
4936 if (prefs_common
.enable_aspell
&& prefs_common
.dictionary
&&
4937 strcmp(prefs_common
.dictionary
, _("None"))) {
4938 gtkaspell
= gtkaspell_new((const gchar
*)prefs_common
.dictionary
,
4939 conv_get_current_charset_str(),
4940 prefs_common
.misspelled_col
,
4941 prefs_common
.check_while_typing
,
4942 prefs_common
.use_alternate
,
4945 alertpanel_error(_("Spell checker could not be started.\n%s"), gtkaspellcheckers
->error_message
);
4946 gtkaspell_checkers_reset_error();
4949 GtkWidget
*menuitem
;
4951 if (!gtkaspell_set_sug_mode(gtkaspell
, prefs_common
.aspell_sugmode
)) {
4952 debug_print("Aspell: could not set suggestion mode %s\n",
4953 gtkaspellcheckers
->error_message
);
4954 gtkaspell_checkers_reset_error();
4957 menuitem
= gtk_item_factory_get_item(ifactory
, "/Spelling/Spelling Configuration");
4958 gtkaspell_populate_submenu(gtkaspell
, menuitem
);
4959 menu_set_sensitive(ifactory
, "/Spelling", TRUE
);
4965 compose_select_account(compose
, account
);
4968 compose
->gtkaspell
= gtkaspell
;
4971 if (account
->set_autocc
&& account
->auto_cc
&& mode
!= COMPOSE_REEDIT
)
4972 compose_entry_append(compose
, account
->auto_cc
, COMPOSE_CC
);
4974 if (account
->set_autobcc
)
4975 compose_entry_append(compose
, account
->auto_bcc
, COMPOSE_BCC
);
4977 if (account
->set_autoreplyto
&& account
->auto_replyto
&& mode
!= COMPOSE_REEDIT
)
4978 compose_entry_append(compose
, account
->auto_replyto
, COMPOSE_REPLYTO
);
4981 if (account
->protocol
!= A_NNTP
)
4982 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose
->header_last
->combo
)->entry
), prefs_common
.trans_hdr
? _("To:") : "To:");
4984 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose
->header_last
->combo
)->entry
), prefs_common
.trans_hdr
? _("Newsgroups:") : "Newsgroups:");
4986 addressbook_set_target_compose(compose
);
4987 update_compose_actions_menu(ifactory
, "/Tools/Actions", compose
);
4989 if (mode
!= COMPOSE_REDIRECT
)
4990 compose_set_template_menu(compose
);
4992 GtkWidget
*menuitem
;
4993 menuitem
= gtk_item_factory_get_item(ifactory
, "/Tools/Template");
4994 menu_set_sensitive(ifactory
, "/Tools/Template", FALSE
);
4997 compose_list
= g_list_append(compose_list
, compose
);
4999 if (!prefs_common
.show_ruler
)
5000 gtk_widget_hide(ruler_hbox
);
5003 compose
->priority
= PRIORITY_NORMAL
;
5004 compose_update_priority_menu_item(compose
);
5007 activate_gnupg_mode(compose
, account
);
5009 toolbar_set_style(compose
->toolbar
->toolbar
, compose
->handlebox
, prefs_common
.toolbar_style
);
5010 gtk_widget_show(window
);
5015 static GtkWidget
*compose_account_option_menu_create(Compose
*compose
)
5021 gint num
= 0, def_menu
= 0;
5023 accounts
= account_get_list();
5024 g_return_val_if_fail(accounts
!= NULL
, NULL
);
5026 hbox
= gtk_hbox_new(FALSE
, 0);
5027 optmenu
= gtk_option_menu_new();
5028 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, FALSE
, FALSE
, 0);
5029 menu
= gtk_menu_new();
5031 for (; accounts
!= NULL
; accounts
= accounts
->next
, num
++) {
5032 PrefsAccount
*ac
= (PrefsAccount
*)accounts
->data
;
5033 GtkWidget
*menuitem
;
5036 if (ac
== compose
->account
) def_menu
= num
;
5039 name
= g_strdup_printf("%s: %s <%s>",
5041 ac
->name
, ac
->address
);
5043 name
= g_strdup_printf("%s: %s",
5044 ac
->account_name
, ac
->address
);
5045 MENUITEM_ADD(menu
, menuitem
, name
, ac
->account_id
);
5047 gtk_signal_connect(GTK_OBJECT(menuitem
), "activate",
5048 GTK_SIGNAL_FUNC(account_activated
),
5052 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu
), menu
);
5053 gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu
), def_menu
);
5058 static void compose_set_priority_cb(gpointer data
,
5062 Compose
*compose
= (Compose
*) data
;
5063 compose
->priority
= action
;
5066 static void compose_update_priority_menu_item(Compose
* compose
)
5068 GtkItemFactory
*ifactory
;
5069 GtkWidget
*menuitem
= NULL
;
5071 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
5073 switch (compose
->priority
) {
5074 case PRIORITY_HIGHEST
:
5075 menuitem
= gtk_item_factory_get_item
5076 (ifactory
, "/Message/Priority/Highest");
5079 menuitem
= gtk_item_factory_get_item
5080 (ifactory
, "/Message/Priority/High");
5082 case PRIORITY_NORMAL
:
5083 menuitem
= gtk_item_factory_get_item
5084 (ifactory
, "/Message/Priority/Normal");
5087 menuitem
= gtk_item_factory_get_item
5088 (ifactory
, "/Message/Priority/Low");
5090 case PRIORITY_LOWEST
:
5091 menuitem
= gtk_item_factory_get_item
5092 (ifactory
, "/Message/Priority/Lowest");
5095 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
5099 static void compose_set_gnupg_mode_cb(gpointer data
,
5103 Compose
*compose
= (Compose
*) data
;
5104 compose
->gnupg_mode
= action
;
5107 static void compose_update_gnupg_mode_menu_item(Compose
* compose
)
5109 GtkItemFactory
*ifactory
;
5110 GtkWidget
*menuitem
= NULL
;
5112 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
5114 switch (compose
->gnupg_mode
) {
5115 case GNUPG_MODE_DETACH
:
5116 menuitem
= gtk_item_factory_get_item
5117 (ifactory
, "/Message/Mode/MIME");
5119 case GNUPG_MODE_INLINE
:
5120 menuitem
= gtk_item_factory_get_item
5121 (ifactory
, "/Message/Mode/Inline");
5124 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem
), TRUE
);
5126 if (compose
->use_encryption
== TRUE
|| compose
->use_signing
== TRUE
)
5127 menu_set_sensitive(ifactory
, "/Message/Mode", TRUE
);
5129 menu_set_sensitive(ifactory
, "/Message/Mode", FALSE
);
5133 static void compose_set_template_menu(Compose
*compose
)
5135 GSList
*tmpl_list
, *cur
;
5139 tmpl_list
= template_get_config();
5141 menu
= gtk_menu_new();
5143 for (cur
= tmpl_list
; cur
!= NULL
; cur
= cur
->next
) {
5144 Template
*tmpl
= (Template
*)cur
->data
;
5146 item
= gtk_menu_item_new_with_label(tmpl
->name
);
5147 gtk_menu_append(GTK_MENU(menu
), item
);
5148 gtk_signal_connect(GTK_OBJECT(item
), "activate",
5149 GTK_SIGNAL_FUNC(compose_template_activate_cb
),
5151 gtk_object_set_data(GTK_OBJECT(item
), "template", tmpl
);
5152 gtk_widget_show(item
);
5155 gtk_widget_show(menu
);
5156 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose
->tmpl_menu
), menu
);
5159 void compose_reflect_prefs_all(void)
5164 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
5165 compose
= (Compose
*)cur
->data
;
5166 compose_set_template_menu(compose
);
5170 void compose_reflect_prefs_pixmap_theme(void)
5175 for (cur
= compose_list
; cur
!= NULL
; cur
= cur
->next
) {
5176 compose
= (Compose
*)cur
->data
;
5177 toolbar_update(TOOLBAR_COMPOSE
, compose
);
5181 static void compose_template_apply(Compose
*compose
, Template
*tmpl
,
5187 if (!tmpl
|| !tmpl
->value
) return;
5189 gtk_stext_freeze(GTK_STEXT(compose
->text
));
5191 if (tmpl
->subject
&& *tmpl
->subject
!= '\0')
5192 gtk_entry_set_text(GTK_ENTRY(compose
->subject_entry
),
5194 if (tmpl
->to
&& *tmpl
->to
!= '\0')
5195 compose_entry_append(compose
, tmpl
->to
, COMPOSE_TO
);
5196 if (tmpl
->cc
&& *tmpl
->cc
!= '\0')
5197 compose_entry_append(compose
, tmpl
->cc
, COMPOSE_CC
);
5199 if (tmpl
->bcc
&& *tmpl
->bcc
!= '\0')
5200 compose_entry_append(compose
, tmpl
->bcc
, COMPOSE_BCC
);
5203 gtk_stext_clear(GTK_STEXT(compose
->text
));
5205 if (compose
->replyinfo
== NULL
) {
5206 parsed_str
= compose_quote_fmt(compose
, NULL
, tmpl
->value
,
5209 if (prefs_common
.quotemark
&& *prefs_common
.quotemark
)
5210 qmark
= prefs_common
.quotemark
;
5214 parsed_str
= compose_quote_fmt(compose
, compose
->replyinfo
,
5215 tmpl
->value
, qmark
, NULL
);
5218 if (replace
&& parsed_str
&& prefs_common
.auto_sig
)
5219 compose_insert_sig(compose
);
5221 if (replace
&& parsed_str
) {
5222 gtk_editable_set_position(GTK_EDITABLE(compose
->text
), 0);
5223 gtk_stext_set_point(GTK_STEXT(compose
->text
), 0);
5227 compose_changed_cb(NULL
, compose
);
5229 gtk_stext_thaw(GTK_STEXT(compose
->text
));
5232 static void compose_destroy(Compose
*compose
)
5235 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
5238 /* NOTE: address_completion_end() does nothing with the window
5239 * however this may change. */
5240 address_completion_end(compose
->window
);
5242 slist_free_strings(compose
->to_list
);
5243 g_slist_free(compose
->to_list
);
5244 slist_free_strings(compose
->newsgroup_list
);
5245 g_slist_free(compose
->newsgroup_list
);
5246 slist_free_strings(compose
->header_list
);
5247 g_slist_free(compose
->header_list
);
5249 procmsg_msginfo_free(compose
->targetinfo
);
5250 procmsg_msginfo_free(compose
->replyinfo
);
5252 g_free(compose
->replyto
);
5253 g_free(compose
->cc
);
5254 g_free(compose
->bcc
);
5255 g_free(compose
->newsgroups
);
5256 g_free(compose
->followup_to
);
5258 g_free(compose
->ml_post
);
5260 g_free(compose
->inreplyto
);
5261 g_free(compose
->references
);
5262 g_free(compose
->msgid
);
5263 g_free(compose
->boundary
);
5265 if (compose
->redirect_filename
)
5266 g_free(compose
->redirect_filename
);
5268 g_free(compose
->exteditor_file
);
5270 for (row
= 0; (ainfo
= gtk_clist_get_row_data(clist
, row
)) != NULL
;
5272 compose_attach_info_free(ainfo
);
5274 if (addressbook_get_target_compose() == compose
)
5275 addressbook_set_target_compose(NULL
);
5278 if (compose
->gtkaspell
) {
5279 gtkaspell_delete(compose
->gtkaspell
);
5283 prefs_common
.compose_width
= compose
->scrolledwin
->allocation
.width
;
5284 prefs_common
.compose_height
= compose
->window
->allocation
.height
;
5286 gtk_widget_destroy(compose
->paned
);
5288 toolbar_destroy(compose
->toolbar
);
5289 g_free(compose
->toolbar
);
5292 compose_list
= g_list_remove(compose_list
, compose
);
5295 static void compose_attach_info_free(AttachInfo
*ainfo
)
5297 g_free(ainfo
->file
);
5298 g_free(ainfo
->content_type
);
5299 g_free(ainfo
->name
);
5303 static void compose_attach_remove_selected(Compose
*compose
)
5305 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
5309 while (clist
->selection
!= NULL
) {
5310 row
= GPOINTER_TO_INT(clist
->selection
->data
);
5311 ainfo
= gtk_clist_get_row_data(clist
, row
);
5312 compose_attach_info_free(ainfo
);
5313 gtk_clist_remove(clist
, row
);
5317 static struct _AttachProperty
5320 GtkWidget
*mimetype_entry
;
5321 GtkWidget
*encoding_optmenu
;
5322 GtkWidget
*path_entry
;
5323 GtkWidget
*filename_entry
;
5325 GtkWidget
*cancel_btn
;
5328 static void compose_attach_property(Compose
*compose
)
5330 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
5333 GtkOptionMenu
*optmenu
;
5334 static gboolean cancelled
;
5336 if (!clist
->selection
) return;
5337 row
= GPOINTER_TO_INT(clist
->selection
->data
);
5339 ainfo
= gtk_clist_get_row_data(clist
, row
);
5342 if (!attach_prop
.window
)
5343 compose_attach_property_create(&cancelled
);
5344 gtk_widget_grab_focus(attach_prop
.ok_btn
);
5345 gtk_widget_show(attach_prop
.window
);
5346 manage_window_set_transient(GTK_WINDOW(attach_prop
.window
));
5348 optmenu
= GTK_OPTION_MENU(attach_prop
.encoding_optmenu
);
5349 if (ainfo
->encoding
== ENC_UNKNOWN
)
5350 gtk_option_menu_set_history(optmenu
, ENC_BASE64
);
5352 gtk_option_menu_set_history(optmenu
, ainfo
->encoding
);
5354 gtk_entry_set_text(GTK_ENTRY(attach_prop
.mimetype_entry
),
5355 ainfo
->content_type
? ainfo
->content_type
: "");
5356 gtk_entry_set_text(GTK_ENTRY(attach_prop
.path_entry
),
5357 ainfo
->file
? ainfo
->file
: "");
5358 gtk_entry_set_text(GTK_ENTRY(attach_prop
.filename_entry
),
5359 ainfo
->name
? ainfo
->name
: "");
5363 gchar
*cnttype
= NULL
;
5367 GtkWidget
*menuitem
;
5372 if (cancelled
== TRUE
) {
5373 gtk_widget_hide(attach_prop
.window
);
5377 text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.mimetype_entry
));
5378 if (*text
!= '\0') {
5381 text
= g_strstrip(g_strdup(text
));
5382 if ((p
= strchr(text
, '/')) && !strchr(p
+ 1, '/')) {
5383 cnttype
= g_strdup(text
);
5386 alertpanel_error(_("Invalid MIME type."));
5392 menu
= gtk_option_menu_get_menu(optmenu
);
5393 menuitem
= gtk_menu_get_active(GTK_MENU(menu
));
5394 ainfo
->encoding
= GPOINTER_TO_INT
5395 (gtk_object_get_user_data(GTK_OBJECT(menuitem
)));
5397 text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.path_entry
));
5398 if (*text
!= '\0') {
5399 if (is_file_exist(text
) &&
5400 (size
= get_file_size(text
)) > 0)
5401 file
= g_strdup(text
);
5404 (_("File doesn't exist or is empty."));
5410 text
= gtk_entry_get_text(GTK_ENTRY(attach_prop
.filename_entry
));
5411 if (*text
!= '\0') {
5412 g_free(ainfo
->name
);
5413 ainfo
->name
= g_strdup(text
);
5417 g_free(ainfo
->content_type
);
5418 ainfo
->content_type
= cnttype
;
5421 g_free(ainfo
->file
);
5427 gtk_clist_set_text(clist
, row
, COL_MIMETYPE
,
5428 ainfo
->content_type
);
5429 gtk_clist_set_text(clist
, row
, COL_SIZE
,
5430 to_human_readable(ainfo
->size
));
5431 gtk_clist_set_text(clist
, row
, COL_NAME
, ainfo
->name
);
5433 gtk_widget_hide(attach_prop
.window
);
5438 #define SET_LABEL_AND_ENTRY(str, entry, top) \
5440 label = gtk_label_new(str); \
5441 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
5442 GTK_FILL, 0, 0, 0); \
5443 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
5445 entry = gtk_entry_new(); \
5446 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
5447 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
5450 static void compose_attach_property_create(gboolean
*cancelled
)
5456 GtkWidget
*mimetype_entry
;
5459 GtkWidget
*optmenu_menu
;
5460 GtkWidget
*menuitem
;
5461 GtkWidget
*path_entry
;
5462 GtkWidget
*filename_entry
;
5465 GtkWidget
*cancel_btn
;
5466 GList
*mime_type_list
, *strlist
;
5468 debug_print("Creating attach_property window...\n");
5470 window
= gtk_window_new(GTK_WINDOW_DIALOG
);
5471 gtk_widget_set_usize(window
, 480, -1);
5472 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
5473 gtk_window_set_title(GTK_WINDOW(window
), _("Property"));
5474 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
5475 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
5476 gtk_signal_connect(GTK_OBJECT(window
), "delete_event",
5477 GTK_SIGNAL_FUNC(attach_property_delete_event
),
5479 gtk_signal_connect(GTK_OBJECT(window
), "key_press_event",
5480 GTK_SIGNAL_FUNC(attach_property_key_pressed
),
5483 vbox
= gtk_vbox_new(FALSE
, 8);
5484 gtk_container_add(GTK_CONTAINER(window
), vbox
);
5486 table
= gtk_table_new(4, 2, FALSE
);
5487 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
5488 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
5489 gtk_table_set_col_spacings(GTK_TABLE(table
), 8);
5491 label
= gtk_label_new(_("MIME type"));
5492 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, (0 + 1),
5494 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
5495 mimetype_entry
= gtk_combo_new();
5496 gtk_table_attach(GTK_TABLE(table
), mimetype_entry
, 1, 2, 0, (0 + 1),
5497 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
5499 /* stuff with list */
5500 mime_type_list
= procmime_get_mime_type_list();
5502 for (; mime_type_list
!= NULL
; mime_type_list
= mime_type_list
->next
) {
5503 MimeType
*type
= (MimeType
*) mime_type_list
->data
;
5504 strlist
= g_list_append(strlist
,
5505 g_strdup_printf("%s/%s",
5506 type
->type
, type
->sub_type
));
5509 gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry
), strlist
);
5511 for (mime_type_list
= strlist
; mime_type_list
!= NULL
;
5512 mime_type_list
= mime_type_list
->next
)
5513 g_free(mime_type_list
->data
);
5514 g_list_free(strlist
);
5516 mimetype_entry
= GTK_COMBO(mimetype_entry
)->entry
;
5518 label
= gtk_label_new(_("Encoding"));
5519 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2,
5521 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
5523 hbox
= gtk_hbox_new(FALSE
, 0);
5524 gtk_table_attach(GTK_TABLE(table
), hbox
, 1, 2, 1, 2,
5525 GTK_EXPAND
|GTK_SHRINK
|GTK_FILL
, 0, 0, 0);
5527 optmenu
= gtk_option_menu_new();
5528 gtk_box_pack_start(GTK_BOX(hbox
), optmenu
, TRUE
, TRUE
, 0);
5530 optmenu_menu
= gtk_menu_new();
5531 MENUITEM_ADD(optmenu_menu
, menuitem
, "7bit", ENC_7BIT
);
5532 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu
), optmenu_menu
);
5534 gtk_widget_set_sensitive(menuitem
, FALSE
);
5536 MENUITEM_ADD(optmenu_menu
, menuitem
, "8bit", ENC_8BIT
);
5537 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu
), optmenu_menu
);
5539 gtk_widget_set_sensitive(menuitem
, FALSE
);
5541 MENUITEM_ADD(optmenu_menu
, menuitem
, "quoted-printable", ENC_QUOTED_PRINTABLE
);
5542 gtk_widget_set_sensitive(menuitem
, FALSE
);
5543 MENUITEM_ADD(optmenu_menu
, menuitem
, "base64", ENC_BASE64
);
5545 gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu
), optmenu_menu
);
5547 SET_LABEL_AND_ENTRY(_("Path"), path_entry
, 2);
5548 SET_LABEL_AND_ENTRY(_("File name"), filename_entry
, 3);
5550 gtkut_button_set_create(&hbbox
, &ok_btn
, _("OK"),
5551 &cancel_btn
, _("Cancel"), NULL
, NULL
);
5552 gtk_box_pack_end(GTK_BOX(vbox
), hbbox
, FALSE
, FALSE
, 0);
5553 gtk_widget_grab_default(ok_btn
);
5555 gtk_signal_connect(GTK_OBJECT(ok_btn
), "clicked",
5556 GTK_SIGNAL_FUNC(attach_property_ok
),
5558 gtk_signal_connect(GTK_OBJECT(cancel_btn
), "clicked",
5559 GTK_SIGNAL_FUNC(attach_property_cancel
),
5562 gtk_widget_show_all(vbox
);
5564 attach_prop
.window
= window
;
5565 attach_prop
.mimetype_entry
= mimetype_entry
;
5566 attach_prop
.encoding_optmenu
= optmenu
;
5567 attach_prop
.path_entry
= path_entry
;
5568 attach_prop
.filename_entry
= filename_entry
;
5569 attach_prop
.ok_btn
= ok_btn
;
5570 attach_prop
.cancel_btn
= cancel_btn
;
5573 #undef SET_LABEL_AND_ENTRY
5575 static void attach_property_ok(GtkWidget
*widget
, gboolean
*cancelled
)
5581 static void attach_property_cancel(GtkWidget
*widget
, gboolean
*cancelled
)
5587 static gint
attach_property_delete_event(GtkWidget
*widget
, GdkEventAny
*event
,
5588 gboolean
*cancelled
)
5596 static void attach_property_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
5597 gboolean
*cancelled
)
5599 if (event
&& event
->keyval
== GDK_Escape
) {
5605 static void compose_exec_ext_editor(Compose
*compose
)
5611 tmp
= g_strdup_printf("%s%ctmpmsg.%08x", get_tmp_dir(),
5612 G_DIR_SEPARATOR
, (gint
)compose
);
5614 if (pipe(pipe_fds
) < 0) {
5620 if ((pid
= fork()) < 0) {
5627 /* close the write side of the pipe */
5630 compose
->exteditor_file
= g_strdup(tmp
);
5631 compose
->exteditor_pid
= pid
;
5632 compose
->exteditor_readdes
= pipe_fds
[0];
5634 compose_set_ext_editor_sensitive(compose
, FALSE
);
5636 compose
->exteditor_tag
=
5637 gdk_input_add(pipe_fds
[0], GDK_INPUT_READ
,
5638 compose_input_cb
, compose
);
5639 } else { /* process-monitoring process */
5645 /* close the read side of the pipe */
5648 if (compose_write_body_to_file(compose
, tmp
) < 0) {
5649 fd_write(pipe_fds
[1], "2\n", 2);
5653 pid_ed
= compose_exec_ext_editor_real(tmp
);
5655 fd_write(pipe_fds
[1], "1\n", 2);
5659 /* wait until editor is terminated */
5660 waitpid(pid_ed
, NULL
, 0);
5662 fd_write(pipe_fds
[1], "0\n", 2);
5671 static gint
compose_exec_ext_editor_real(const gchar
*file
)
5673 static gchar
*def_cmd
= "emacs %s";
5679 g_return_val_if_fail(file
!= NULL
, -1);
5681 if ((pid
= fork()) < 0) {
5686 if (pid
!= 0) return pid
;
5688 /* grandchild process */
5690 if (setpgid(0, getppid()))
5693 if (prefs_common
.ext_editor_cmd
&&
5694 (p
= strchr(prefs_common
.ext_editor_cmd
, '%')) &&
5695 *(p
+ 1) == 's' && !strchr(p
+ 2, '%')) {
5696 g_snprintf(buf
, sizeof(buf
), prefs_common
.ext_editor_cmd
, file
);
5698 if (prefs_common
.ext_editor_cmd
)
5699 g_warning("External editor command line is invalid: `%s'\n",
5700 prefs_common
.ext_editor_cmd
);
5701 g_snprintf(buf
, sizeof(buf
), def_cmd
, file
);
5704 cmdline
= strsplit_with_quote(buf
, " ", 1024);
5705 execvp(cmdline
[0], cmdline
);
5708 g_strfreev(cmdline
);
5713 static gboolean
compose_ext_editor_kill(Compose
*compose
)
5715 pid_t pgid
= compose
->exteditor_pid
* -1;
5718 ret
= kill(pgid
, 0);
5720 if (ret
== 0 || (ret
== -1 && EPERM
== errno
)) {
5724 msg
= g_strdup_printf
5725 (_("The external editor is still working.\n"
5726 "Force terminating the process?\n"
5727 "process group id: %d"), -pgid
);
5728 val
= alertpanel(_("Notice"), msg
, _("Yes"), _("+No"), NULL
);
5731 if (val
== G_ALERTDEFAULT
) {
5732 gdk_input_remove(compose
->exteditor_tag
);
5733 close(compose
->exteditor_readdes
);
5735 if (kill(pgid
, SIGTERM
) < 0) perror("kill");
5736 waitpid(compose
->exteditor_pid
, NULL
, 0);
5738 g_warning("Terminated process group id: %d", -pgid
);
5739 g_warning("Temporary file: %s",
5740 compose
->exteditor_file
);
5742 compose_set_ext_editor_sensitive(compose
, TRUE
);
5744 g_free(compose
->exteditor_file
);
5745 compose
->exteditor_file
= NULL
;
5746 compose
->exteditor_pid
= -1;
5747 compose
->exteditor_readdes
= -1;
5748 compose
->exteditor_tag
= -1;
5756 static void compose_input_cb(gpointer data
, gint source
,
5757 GdkInputCondition condition
)
5760 Compose
*compose
= (Compose
*)data
;
5763 debug_print("Compose: input from monitoring process\n");
5765 gdk_input_remove(compose
->exteditor_tag
);
5768 if (read(source
, &buf
[i
], 1) < 1) {
5772 if (buf
[i
] == '\n') {
5777 if (i
== sizeof(buf
) - 1)
5781 waitpid(compose
->exteditor_pid
, NULL
, 0);
5783 if (buf
[0] == '0') { /* success */
5784 GtkSText
*text
= GTK_STEXT(compose
->text
);
5786 gtk_stext_freeze(text
);
5787 gtk_stext_set_point(text
, 0);
5788 gtk_stext_forward_delete(text
, gtk_stext_get_length(text
));
5789 compose_insert_file(compose
, compose
->exteditor_file
);
5790 compose_changed_cb(NULL
, compose
);
5791 gtk_stext_thaw(text
);
5793 if (unlink(compose
->exteditor_file
) < 0)
5794 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
5795 } else if (buf
[0] == '1') { /* failed */
5796 g_warning("Couldn't exec external editor\n");
5797 if (unlink(compose
->exteditor_file
) < 0)
5798 FILE_OP_ERROR(compose
->exteditor_file
, "unlink");
5799 } else if (buf
[0] == '2') {
5800 g_warning("Couldn't write to file\n");
5801 } else if (buf
[0] == '3') {
5802 g_warning("Pipe read failed\n");
5807 compose_set_ext_editor_sensitive(compose
, TRUE
);
5809 g_free(compose
->exteditor_file
);
5810 compose
->exteditor_file
= NULL
;
5811 compose
->exteditor_pid
= -1;
5812 compose
->exteditor_readdes
= -1;
5813 compose
->exteditor_tag
= -1;
5816 static void compose_set_ext_editor_sensitive(Compose
*compose
,
5819 GtkItemFactory
*ifactory
;
5821 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
5823 menu_set_sensitive(ifactory
, "/Message/Send", sensitive
);
5824 menu_set_sensitive(ifactory
, "/Message/Send later", sensitive
);
5825 menu_set_sensitive(ifactory
, "/Message/Save to draft folder",
5827 menu_set_sensitive(ifactory
, "/File/Insert file", sensitive
);
5828 menu_set_sensitive(ifactory
, "/File/Insert signature", sensitive
);
5829 menu_set_sensitive(ifactory
, "/Edit/Wrap current paragraph", sensitive
);
5830 menu_set_sensitive(ifactory
, "/Edit/Wrap all long lines", sensitive
);
5831 menu_set_sensitive(ifactory
, "/Edit/Edit with external editor",
5834 gtk_widget_set_sensitive(compose
->text
, sensitive
);
5835 gtk_widget_set_sensitive(compose
->toolbar
->send_btn
, sensitive
);
5836 gtk_widget_set_sensitive(compose
->toolbar
->sendl_btn
, sensitive
);
5837 gtk_widget_set_sensitive(compose
->toolbar
->draft_btn
, sensitive
);
5838 gtk_widget_set_sensitive(compose
->toolbar
->insert_btn
, sensitive
);
5839 gtk_widget_set_sensitive(compose
->toolbar
->sig_btn
, sensitive
);
5840 gtk_widget_set_sensitive(compose
->toolbar
->exteditor_btn
, sensitive
);
5841 gtk_widget_set_sensitive(compose
->toolbar
->linewrap_btn
, sensitive
);
5845 * compose_undo_state_changed:
5847 * Change the sensivity of the menuentries undo and redo
5849 static void compose_undo_state_changed(UndoMain
*undostruct
, gint undo_state
,
5850 gint redo_state
, gpointer data
)
5852 GtkWidget
*widget
= GTK_WIDGET(data
);
5853 GtkItemFactory
*ifactory
;
5855 g_return_if_fail(widget
!= NULL
);
5857 debug_print("Set_undo. UNDO:%i REDO:%i\n", undo_state
, redo_state
);
5859 ifactory
= gtk_item_factory_from_widget(widget
);
5861 switch (undo_state
) {
5862 case UNDO_STATE_TRUE
:
5863 if (!undostruct
->undo_state
) {
5864 debug_print ("Set_undo - Testpoint\n");
5865 undostruct
->undo_state
= TRUE
;
5866 menu_set_sensitive(ifactory
, "/Edit/Undo", TRUE
);
5869 case UNDO_STATE_FALSE
:
5870 if (undostruct
->undo_state
) {
5871 undostruct
->undo_state
= FALSE
;
5872 menu_set_sensitive(ifactory
, "/Edit/Undo", FALSE
);
5875 case UNDO_STATE_UNCHANGED
:
5877 case UNDO_STATE_REFRESH
:
5878 menu_set_sensitive(ifactory
, "/Edit/Undo",
5879 undostruct
->undo_state
);
5882 g_warning("Undo state not recognized");
5886 switch (redo_state
) {
5887 case UNDO_STATE_TRUE
:
5888 if (!undostruct
->redo_state
) {
5889 undostruct
->redo_state
= TRUE
;
5890 menu_set_sensitive(ifactory
, "/Edit/Redo", TRUE
);
5893 case UNDO_STATE_FALSE
:
5894 if (undostruct
->redo_state
) {
5895 undostruct
->redo_state
= FALSE
;
5896 menu_set_sensitive(ifactory
, "/Edit/Redo", FALSE
);
5899 case UNDO_STATE_UNCHANGED
:
5901 case UNDO_STATE_REFRESH
:
5902 menu_set_sensitive(ifactory
, "/Edit/Redo",
5903 undostruct
->redo_state
);
5906 g_warning("Redo state not recognized");
5911 static gint
calc_cursor_xpos(GtkSText
*text
, gint extra
, gint char_width
)
5915 cursor_pos
= (text
->cursor_pos_x
- extra
) / char_width
;
5916 cursor_pos
= MAX(cursor_pos
, 0);
5921 /* callback functions */
5923 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
5924 * includes "non-client" (windows-izm) in calculation, so this calculation
5925 * may not be accurate.
5927 static gboolean
compose_edit_size_alloc(GtkEditable
*widget
,
5928 GtkAllocation
*allocation
,
5929 GtkSHRuler
*shruler
)
5931 if (prefs_common
.show_ruler
) {
5933 gint line_width_in_chars
;
5935 char_width
= gtkut_get_font_width
5936 (GTK_WIDGET(widget
)->style
->font
);
5937 line_width_in_chars
=
5938 (allocation
->width
- allocation
->x
) / char_width
;
5940 /* got the maximum */
5941 gtk_ruler_set_range(GTK_RULER(shruler
),
5942 0.0, line_width_in_chars
,
5943 calc_cursor_xpos(GTK_STEXT(widget
),
5946 /*line_width_in_chars*/ char_width
);
5952 static void account_activated(GtkMenuItem
*menuitem
, gpointer data
)
5954 Compose
*compose
= (Compose
*)data
;
5958 ac
= account_find_from_id(
5959 GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem
))));
5960 g_return_if_fail(ac
!= NULL
);
5962 if (ac
!= compose
->account
)
5963 compose_select_account(compose
, ac
);
5966 static void attach_selected(GtkCList
*clist
, gint row
, gint column
,
5967 GdkEvent
*event
, gpointer data
)
5969 Compose
*compose
= (Compose
*)data
;
5971 if (event
&& event
->type
== GDK_2BUTTON_PRESS
)
5972 compose_attach_property(compose
);
5975 static void attach_button_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
5978 Compose
*compose
= (Compose
*)data
;
5979 GtkCList
*clist
= GTK_CLIST(compose
->attach_clist
);
5984 if (event
->button
== 3) {
5985 if ((clist
->selection
&& !clist
->selection
->next
) ||
5986 !clist
->selection
) {
5987 gtk_clist_unselect_all(clist
);
5988 if (gtk_clist_get_selection_info(clist
,
5991 gtk_clist_select_row(clist
, row
, column
);
5992 gtkut_clist_set_focus_row(clist
, row
);
5995 gtk_menu_popup(GTK_MENU(compose
->popupmenu
), NULL
, NULL
,
5996 NULL
, NULL
, event
->button
, event
->time
);
6000 static void attach_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
6003 Compose
*compose
= (Compose
*)data
;
6007 switch (event
->keyval
) {
6009 compose_attach_remove_selected(compose
);
6014 static void compose_allow_user_actions (Compose
*compose
, gboolean allow
)
6016 GtkItemFactory
*ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
6017 toolbar_comp_set_sensitive(compose
, allow
);
6018 menu_set_sensitive(ifactory
, "/File", allow
);
6019 menu_set_sensitive(ifactory
, "/Edit", allow
);
6020 menu_set_sensitive(ifactory
, "/Spelling", allow
);
6021 menu_set_sensitive(ifactory
, "/Message", allow
);
6022 menu_set_sensitive(ifactory
, "/Tools", allow
);
6023 menu_set_sensitive(ifactory
, "/Help", allow
);
6026 static void compose_send_cb(gpointer data
, guint action
, GtkWidget
*widget
)
6028 Compose
*compose
= (Compose
*)data
;
6031 if (prefs_common
.work_offline
)
6032 if (alertpanel(_("Offline warning"),
6033 _("You're working offline. Override?"),
6034 _("Yes"), _("No"), NULL
) != G_ALERTDEFAULT
)
6037 compose_allow_user_actions (compose
, FALSE
);
6038 compose
->sending
= TRUE
;
6039 val
= compose_send(compose
);
6040 compose_allow_user_actions (compose
, TRUE
);
6041 compose
->sending
= FALSE
;
6043 if (val
== 0) gtk_widget_destroy(compose
->window
);
6046 static void compose_send_later_cb(gpointer data
, guint action
,
6049 Compose
*compose
= (Compose
*)data
;
6052 val
= compose_queue_sub(compose
, NULL
, NULL
, TRUE
);
6053 if (!val
) gtk_widget_destroy(compose
->window
);
6056 void compose_draft (gpointer data
)
6058 compose_draft_cb(data
, 0, NULL
);
6061 static void compose_draft_cb(gpointer data
, guint action
, GtkWidget
*widget
)
6063 Compose
*compose
= (Compose
*)data
;
6067 static gboolean lock
= FALSE
;
6068 MsgInfo
*newmsginfo
;
6072 draft
= account_get_special_folder(compose
->account
, F_DRAFT
);
6073 g_return_if_fail(draft
!= NULL
);
6077 tmp
= g_strdup_printf("%s%cdraft.%08x", get_tmp_dir(),
6078 G_DIR_SEPARATOR
, (gint
)compose
);
6080 if (compose_write_to_file(compose
, tmp
, TRUE
) < 0) {
6086 if ((msgnum
= folder_item_add_msg(draft
, tmp
, TRUE
)) < 0) {
6093 draft
->mtime
= 0; /* force updating */
6095 if (compose
->mode
== COMPOSE_REEDIT
) {
6096 compose_remove_reedit_target(compose
);
6097 if (compose
->targetinfo
&&
6098 compose
->targetinfo
->folder
!= draft
)
6099 folder_update_item(compose
->targetinfo
->folder
,
6103 newmsginfo
= folder_item_get_msginfo(draft
, msgnum
);
6105 procmsg_msginfo_unset_flags(newmsginfo
, ~0, ~0);
6106 procmsg_msginfo_set_flags(newmsginfo
, 0, MSG_DRAFT
);
6107 folder_update_item(draft
, TRUE
);
6108 procmsg_msginfo_free(newmsginfo
);
6113 /* 0: quit editing 1: keep editing */
6115 gtk_widget_destroy(compose
->window
);
6120 path
= folder_item_fetch_msg(draft
, msgnum
);
6121 g_return_if_fail(path
!= NULL
);
6122 if (stat(path
, &s
) < 0) {
6123 FILE_OP_ERROR(path
, "stat");
6130 procmsg_msginfo_free(compose
->targetinfo
);
6131 compose
->targetinfo
= procmsg_msginfo_new();
6132 compose
->targetinfo
->msgnum
= msgnum
;
6133 compose
->targetinfo
->size
= s
.st_size
;
6134 compose
->targetinfo
->mtime
= s
.st_mtime
;
6135 compose
->targetinfo
->folder
= draft
;
6136 compose
->mode
= COMPOSE_REEDIT
;
6140 static void compose_attach_cb(gpointer data
, guint action
, GtkWidget
*widget
)
6142 Compose
*compose
= (Compose
*)data
;
6145 if (compose
->redirect_filename
!= NULL
)
6148 file_list
= filesel_select_multiple_files(_("Select file"), NULL
);
6153 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
6154 gchar
*file
= (gchar
*) tmp
->data
;
6155 compose_attach_append(compose
, file
, file
, NULL
);
6156 compose_changed_cb(NULL
, compose
);
6159 g_list_free(file_list
);
6163 static void compose_insert_file_cb(gpointer data
, guint action
,
6166 Compose
*compose
= (Compose
*)data
;
6169 file_list
= filesel_select_multiple_files(_("Select file"), NULL
);
6174 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
6175 gchar
*file
= (gchar
*) tmp
->data
;
6176 compose_insert_file(compose
, file
);
6179 g_list_free(file_list
);
6183 static gint
compose_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
6186 Compose
*compose
= (Compose
*)data
;
6187 if (compose
->sending
)
6189 compose_close_cb(compose
, 0, NULL
);
6193 static void compose_close_cb(gpointer data
, guint action
, GtkWidget
*widget
)
6195 Compose
*compose
= (Compose
*)data
;
6198 if (compose
->exteditor_tag
!= -1) {
6199 if (!compose_ext_editor_kill(compose
))
6203 if (compose
->modified
) {
6204 val
= alertpanel(_("Discard message"),
6205 _("This message has been modified. discard it?"),
6206 _("Discard"), _("to Draft"), _("Cancel"));
6209 case G_ALERTDEFAULT
:
6210 if (prefs_common
.autosave
)
6211 compose_remove_draft(compose
);
6213 case G_ALERTALTERNATE
:
6214 compose_draft_cb(data
, 0, NULL
);
6221 gtk_widget_destroy(compose
->window
);
6224 static void compose_address_cb(gpointer data
, guint action
, GtkWidget
*widget
)
6226 Compose
*compose
= (Compose
*)data
;
6228 addressbook_open(compose
);
6231 static void compose_template_activate_cb(GtkWidget
*widget
, gpointer data
)
6233 Compose
*compose
= (Compose
*)data
;
6238 tmpl
= gtk_object_get_data(GTK_OBJECT(widget
), "template");
6239 g_return_if_fail(tmpl
!= NULL
);
6241 msg
= g_strdup_printf(_("Do you want to apply the template `%s' ?"),
6243 val
= alertpanel(_("Apply template"), msg
,
6244 _("Replace"), _("Insert"), _("Cancel"));
6247 if (val
== G_ALERTDEFAULT
)
6248 compose_template_apply(compose
, tmpl
, TRUE
);
6249 else if (val
== G_ALERTALTERNATE
)
6250 compose_template_apply(compose
, tmpl
, FALSE
);
6253 static void compose_ext_editor_cb(gpointer data
, guint action
,
6256 Compose
*compose
= (Compose
*)data
;
6258 compose_exec_ext_editor(compose
);
6261 static void compose_destroy_cb(GtkWidget
*widget
, Compose
*compose
)
6263 if (compose
->sending
)
6265 compose_destroy(compose
);
6268 static void compose_undo_cb(Compose
*compose
)
6270 undo_undo(compose
->undostruct
);
6273 static void compose_redo_cb(Compose
*compose
)
6275 undo_redo(compose
->undostruct
);
6278 static void compose_cut_cb(Compose
*compose
)
6280 if (compose
->focused_editable
&&
6281 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
6282 gtk_editable_cut_clipboard
6283 (GTK_EDITABLE(compose
->focused_editable
));
6286 static void compose_copy_cb(Compose
*compose
)
6288 if (compose
->focused_editable
&&
6289 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
6290 gtk_editable_copy_clipboard
6291 (GTK_EDITABLE(compose
->focused_editable
));
6294 static void compose_paste_cb(Compose
*compose
)
6296 if (compose
->focused_editable
&&
6297 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
6298 gtk_editable_paste_clipboard
6299 (GTK_EDITABLE(compose
->focused_editable
));
6302 static void compose_paste_as_quote_cb(Compose
*compose
)
6304 if (compose
->focused_editable
&&
6305 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
)) {
6306 compose
->paste_as_quotation
= TRUE
;
6307 gtk_editable_paste_clipboard
6308 (GTK_EDITABLE(compose
->focused_editable
));
6309 compose
->paste_as_quotation
= FALSE
;
6313 static void compose_allsel_cb(Compose
*compose
)
6315 if (compose
->focused_editable
&&
6316 GTK_WIDGET_HAS_FOCUS(compose
->focused_editable
))
6317 gtk_editable_select_region
6318 (GTK_EDITABLE(compose
->focused_editable
), 0, -1);
6321 static void compose_gtk_stext_action_cb(Compose
*compose
,
6322 ComposeCallGtkSTextAction action
)
6324 GtkSText
*text
= GTK_STEXT(compose
->text
);
6326 void (*do_action
) (GtkSText
*text
);
6327 } action_table
[] = {
6328 {gtk_stext_move_beginning_of_line
},
6329 {gtk_stext_move_forward_character
},
6330 {gtk_stext_move_backward_character
},
6331 {gtk_stext_move_forward_word
},
6332 {gtk_stext_move_backward_word
},
6333 {gtk_stext_move_end_of_line
},
6334 {gtk_stext_move_next_line
},
6335 {gtk_stext_move_previous_line
},
6336 {gtk_stext_delete_forward_character
},
6337 {gtk_stext_delete_backward_character
},
6338 {gtk_stext_delete_forward_word
},
6339 {gtk_stext_delete_backward_word
},
6340 {gtk_stext_delete_line
},
6341 {gtk_stext_delete_line
}, /* gtk_stext_delete_line_n */
6342 {gtk_stext_delete_to_line_end
}
6345 if (!GTK_WIDGET_HAS_FOCUS(text
)) return;
6347 if (action
>= COMPOSE_CALL_GTK_STEXT_MOVE_BEGINNING_OF_LINE
&&
6348 action
<= COMPOSE_CALL_GTK_STEXT_DELETE_TO_LINE_END
)
6349 action_table
[action
].do_action(text
);
6352 static void compose_grab_focus_cb(GtkWidget
*widget
, Compose
*compose
)
6354 if (GTK_IS_EDITABLE(widget
))
6355 compose
->focused_editable
= widget
;
6358 static void compose_changed_cb(GtkEditable
*editable
, Compose
*compose
)
6360 if (compose
->modified
== FALSE
) {
6361 compose
->modified
= TRUE
;
6362 compose_set_title(compose
);
6366 static void compose_button_press_cb(GtkWidget
*widget
, GdkEventButton
*event
,
6369 gtk_stext_set_point(GTK_STEXT(widget
),
6370 gtk_editable_get_position(GTK_EDITABLE(widget
)));
6374 static void compose_key_press_cb(GtkWidget
*widget
, GdkEventKey
*event
,
6377 gtk_stext_set_point(GTK_STEXT(widget
),
6378 gtk_editable_get_position(GTK_EDITABLE(widget
)));
6382 #if 0 /* NEW COMPOSE GUI */
6383 static void compose_toggle_to_cb(gpointer data
, guint action
,
6386 Compose
*compose
= (Compose
*)data
;
6388 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6389 gtk_widget_show(compose
->to_hbox
);
6390 gtk_widget_show(compose
->to_entry
);
6391 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 1, 4);
6392 compose
->use_to
= TRUE
;
6394 gtk_widget_hide(compose
->to_hbox
);
6395 gtk_widget_hide(compose
->to_entry
);
6396 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 1, 0);
6397 gtk_widget_queue_resize(compose
->table_vbox
);
6398 compose
->use_to
= FALSE
;
6401 if (addressbook_get_target_compose() == compose
)
6402 addressbook_set_target_compose(compose
);
6405 static void compose_toggle_cc_cb(gpointer data
, guint action
,
6408 Compose
*compose
= (Compose
*)data
;
6410 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6411 gtk_widget_show(compose
->cc_hbox
);
6412 gtk_widget_show(compose
->cc_entry
);
6413 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 3, 4);
6414 compose
->use_cc
= TRUE
;
6416 gtk_widget_hide(compose
->cc_hbox
);
6417 gtk_widget_hide(compose
->cc_entry
);
6418 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 3, 0);
6419 gtk_widget_queue_resize(compose
->table_vbox
);
6420 compose
->use_cc
= FALSE
;
6423 if (addressbook_get_target_compose() == compose
)
6424 addressbook_set_target_compose(compose
);
6427 static void compose_toggle_bcc_cb(gpointer data
, guint action
,
6430 Compose
*compose
= (Compose
*)data
;
6432 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6433 gtk_widget_show(compose
->bcc_hbox
);
6434 gtk_widget_show(compose
->bcc_entry
);
6435 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 4, 4);
6436 compose
->use_bcc
= TRUE
;
6438 gtk_widget_hide(compose
->bcc_hbox
);
6439 gtk_widget_hide(compose
->bcc_entry
);
6440 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 4, 0);
6441 gtk_widget_queue_resize(compose
->table_vbox
);
6442 compose
->use_bcc
= FALSE
;
6445 if (addressbook_get_target_compose() == compose
)
6446 addressbook_set_target_compose(compose
);
6449 static void compose_toggle_replyto_cb(gpointer data
, guint action
,
6452 Compose
*compose
= (Compose
*)data
;
6454 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6455 gtk_widget_show(compose
->reply_hbox
);
6456 gtk_widget_show(compose
->reply_entry
);
6457 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 5, 4);
6458 compose
->use_replyto
= TRUE
;
6460 gtk_widget_hide(compose
->reply_hbox
);
6461 gtk_widget_hide(compose
->reply_entry
);
6462 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 5, 0);
6463 gtk_widget_queue_resize(compose
->table_vbox
);
6464 compose
->use_replyto
= FALSE
;
6468 static void compose_toggle_followupto_cb(gpointer data
, guint action
,
6471 Compose
*compose
= (Compose
*)data
;
6473 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6474 gtk_widget_show(compose
->followup_hbox
);
6475 gtk_widget_show(compose
->followup_entry
);
6476 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 6, 4);
6477 compose
->use_followupto
= TRUE
;
6479 gtk_widget_hide(compose
->followup_hbox
);
6480 gtk_widget_hide(compose
->followup_entry
);
6481 gtk_table_set_row_spacing(GTK_TABLE(compose
->table
), 6, 0);
6482 gtk_widget_queue_resize(compose
->table_vbox
);
6483 compose
->use_followupto
= FALSE
;
6487 static void compose_toggle_attach_cb(gpointer data
, guint action
,
6490 Compose
*compose
= (Compose
*)data
;
6492 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6493 gtk_widget_ref(compose
->edit_vbox
);
6495 gtkut_container_remove(GTK_CONTAINER(compose
->vbox2
),
6496 compose
->edit_vbox
);
6497 gtk_paned_add2(GTK_PANED(compose
->paned
), compose
->edit_vbox
);
6498 gtk_box_pack_start(GTK_BOX(compose
->vbox2
), compose
->paned
,
6500 gtk_widget_show(compose
->paned
);
6502 gtk_widget_unref(compose
->edit_vbox
);
6503 gtk_widget_unref(compose
->paned
);
6505 compose
->use_attach
= TRUE
;
6507 gtk_widget_ref(compose
->paned
);
6508 gtk_widget_ref(compose
->edit_vbox
);
6510 gtkut_container_remove(GTK_CONTAINER(compose
->vbox2
),
6512 gtkut_container_remove(GTK_CONTAINER(compose
->paned
),
6513 compose
->edit_vbox
);
6514 gtk_box_pack_start(GTK_BOX(compose
->vbox2
),
6515 compose
->edit_vbox
, TRUE
, TRUE
, 0);
6517 gtk_widget_unref(compose
->edit_vbox
);
6519 compose
->use_attach
= FALSE
;
6525 static void compose_toggle_sign_cb(gpointer data
, guint action
,
6528 Compose
*compose
= (Compose
*)data
;
6530 if (GTK_CHECK_MENU_ITEM(widget
)->active
)
6531 compose
->use_signing
= TRUE
;
6533 compose
->use_signing
= FALSE
;
6535 compose_update_gnupg_mode_menu_item(compose
);
6538 static void compose_toggle_encrypt_cb(gpointer data
, guint action
,
6541 Compose
*compose
= (Compose
*)data
;
6543 if (GTK_CHECK_MENU_ITEM(widget
)->active
)
6544 compose
->use_encryption
= TRUE
;
6546 compose
->use_encryption
= FALSE
;
6548 compose_update_gnupg_mode_menu_item(compose
);
6551 static void activate_gnupg_mode (Compose
*compose
, PrefsAccount
*account
)
6553 if (account
->default_gnupg_mode
)
6554 compose
->gnupg_mode
= GNUPG_MODE_INLINE
;
6556 compose
->gnupg_mode
= GNUPG_MODE_DETACH
;
6558 compose_update_gnupg_mode_menu_item(compose
);
6560 #endif /* USE_GPGME */
6562 static void compose_toggle_ruler_cb(gpointer data
, guint action
,
6565 Compose
*compose
= (Compose
*)data
;
6567 if (GTK_CHECK_MENU_ITEM(widget
)->active
) {
6568 gtk_widget_show(compose
->ruler_hbox
);
6569 prefs_common
.show_ruler
= TRUE
;
6571 gtk_widget_hide(compose
->ruler_hbox
);
6572 gtk_widget_queue_resize(compose
->edit_vbox
);
6573 prefs_common
.show_ruler
= FALSE
;
6577 static void compose_attach_drag_received_cb (GtkWidget
*widget
,
6578 GdkDragContext
*drag_context
,
6581 GtkSelectionData
*data
,
6586 Compose
*compose
= (Compose
*)user_data
;
6589 list
= uri_list_extract_filenames((const gchar
*)data
->data
);
6590 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
)
6591 compose_attach_append
6592 (compose
, (const gchar
*)tmp
->data
,
6593 (const gchar
*)tmp
->data
, NULL
);
6594 if (list
) compose_changed_cb(NULL
, compose
);
6595 list_free_strings(list
);
6599 static void compose_insert_drag_received_cb (GtkWidget
*widget
,
6600 GdkDragContext
*drag_context
,
6603 GtkSelectionData
*data
,
6608 Compose
*compose
= (Compose
*)user_data
;
6611 list
= uri_list_extract_filenames((const gchar
*)data
->data
);
6612 for (tmp
= list
; tmp
!= NULL
; tmp
= tmp
->next
)
6613 compose_insert_file(compose
, (const gchar
*)tmp
->data
);
6614 list_free_strings(list
);
6618 #if 0 /* NEW COMPOSE GUI */
6619 static void to_activated(GtkWidget
*widget
, Compose
*compose
)
6621 if (GTK_WIDGET_VISIBLE(compose
->newsgroups_entry
))
6622 gtk_widget_grab_focus(compose
->newsgroups_entry
);
6624 gtk_widget_grab_focus(compose
->subject_entry
);
6627 static void newsgroups_activated(GtkWidget
*widget
, Compose
*compose
)
6629 gtk_widget_grab_focus(compose
->subject_entry
);
6632 static void subject_activated(GtkWidget
*widget
, Compose
*compose
)
6634 if (GTK_WIDGET_VISIBLE(compose
->cc_entry
))
6635 gtk_widget_grab_focus(compose
->cc_entry
);
6636 else if (GTK_WIDGET_VISIBLE(compose
->bcc_entry
))
6637 gtk_widget_grab_focus(compose
->bcc_entry
);
6638 else if (GTK_WIDGET_VISIBLE(compose
->reply_entry
))
6639 gtk_widget_grab_focus(compose
->reply_entry
);
6640 else if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
6641 gtk_widget_grab_focus(compose
->followup_entry
);
6643 gtk_widget_grab_focus(compose
->text
);
6646 static void cc_activated(GtkWidget
*widget
, Compose
*compose
)
6648 if (GTK_WIDGET_VISIBLE(compose
->bcc_entry
))
6649 gtk_widget_grab_focus(compose
->bcc_entry
);
6650 else if (GTK_WIDGET_VISIBLE(compose
->reply_entry
))
6651 gtk_widget_grab_focus(compose
->reply_entry
);
6652 else if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
6653 gtk_widget_grab_focus(compose
->followup_entry
);
6655 gtk_widget_grab_focus(compose
->text
);
6658 static void bcc_activated(GtkWidget
*widget
, Compose
*compose
)
6660 if (GTK_WIDGET_VISIBLE(compose
->reply_entry
))
6661 gtk_widget_grab_focus(compose
->reply_entry
);
6662 else if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
6663 gtk_widget_grab_focus(compose
->followup_entry
);
6665 gtk_widget_grab_focus(compose
->text
);
6668 static void replyto_activated(GtkWidget
*widget
, Compose
*compose
)
6670 if (GTK_WIDGET_VISIBLE(compose
->followup_entry
))
6671 gtk_widget_grab_focus(compose
->followup_entry
);
6673 gtk_widget_grab_focus(compose
->text
);
6676 static void followupto_activated(GtkWidget
*widget
, Compose
*compose
)
6678 gtk_widget_grab_focus(compose
->text
);
6682 static void compose_toggle_return_receipt_cb(gpointer data
, guint action
,
6685 Compose
*compose
= (Compose
*)data
;
6687 if (GTK_CHECK_MENU_ITEM(widget
)->active
)
6688 compose
->return_receipt
= TRUE
;
6690 compose
->return_receipt
= FALSE
;
6693 void compose_headerentry_key_press_event_cb(GtkWidget
*entry
,
6695 ComposeHeaderEntry
*headerentry
)
6697 if ((g_slist_length(headerentry
->compose
->header_list
) > 0) &&
6698 ((headerentry
->headernum
+ 1) != headerentry
->compose
->header_nextrow
) &&
6699 !(event
->state
& GDK_MODIFIER_MASK
) &&
6700 (event
->keyval
== GDK_BackSpace
) &&
6701 (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) == 0)) {
6702 gtk_container_remove
6703 (GTK_CONTAINER(headerentry
->compose
->header_table
),
6704 headerentry
->combo
);
6705 gtk_container_remove
6706 (GTK_CONTAINER(headerentry
->compose
->header_table
),
6707 headerentry
->entry
);
6708 headerentry
->compose
->header_list
=
6709 g_slist_remove(headerentry
->compose
->header_list
,
6711 g_free(headerentry
);
6712 } else if (event
->keyval
== GDK_Tab
) {
6713 if (headerentry
->compose
->header_last
== headerentry
) {
6714 /* Override default next focus, and give it to subject_entry
6715 * instead of notebook tabs
6717 gtk_signal_emit_stop_by_name(GTK_OBJECT(entry
), "key-press-event");
6718 gtk_widget_grab_focus(headerentry
->compose
->subject_entry
);
6724 void compose_headerentry_changed_cb(GtkWidget
*entry
,
6725 ComposeHeaderEntry
*headerentry
)
6727 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry
))) != 0) {
6728 headerentry
->compose
->header_list
=
6729 g_slist_append(headerentry
->compose
->header_list
,
6731 compose_create_header_entry(headerentry
->compose
);
6732 gtk_signal_disconnect_by_func
6734 GTK_SIGNAL_FUNC(compose_headerentry_changed_cb
),
6736 /* Automatically scroll down */
6737 compose_show_first_last_header(headerentry
->compose
, FALSE
);
6742 static void compose_show_first_last_header(Compose
*compose
, gboolean show_first
)
6744 GtkAdjustment
*vadj
;
6746 g_return_if_fail(compose
);
6747 g_return_if_fail(GTK_IS_WIDGET(compose
->header_table
));
6748 g_return_if_fail(GTK_IS_VIEWPORT(compose
->header_table
->parent
));
6750 vadj
= gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose
->header_table
->parent
));
6751 gtk_adjustment_set_value(vadj
, (show_first
? vadj
->lower
: vadj
->upper
));
6754 static void text_activated(GtkWidget
*widget
, Compose
*compose
)
6756 compose_send_control_enter(compose
);
6759 static void text_inserted(GtkWidget
*widget
, const gchar
*text
,
6760 gint length
, gint
*position
, Compose
*compose
)
6762 GtkEditable
*editable
= GTK_EDITABLE(widget
);
6764 gtk_signal_handler_block_by_func(GTK_OBJECT(widget
),
6765 GTK_SIGNAL_FUNC(text_inserted
),
6767 if (compose
->paste_as_quotation
) {
6772 new_text
= g_strndup(text
, length
);
6773 if (prefs_common
.quotemark
&& *prefs_common
.quotemark
)
6774 qmark
= prefs_common
.quotemark
;
6777 gtk_stext_set_point(GTK_STEXT(widget
), *position
);
6778 compose_quote_fmt(compose
, NULL
, "%Q", qmark
, new_text
);
6779 pos
= gtk_stext_get_point(GTK_STEXT(widget
));
6780 gtk_editable_set_position(editable
, pos
);
6784 gtk_editable_insert_text(editable
, text
, length
, position
);
6786 if (prefs_common
.autowrap
)
6787 compose_wrap_line_all_full(compose
, TRUE
);
6789 gtk_signal_handler_unblock_by_func(GTK_OBJECT(widget
),
6790 GTK_SIGNAL_FUNC(text_inserted
),
6792 gtk_signal_emit_stop_by_name(GTK_OBJECT(editable
), "insert_text");
6795 if (prefs_common
.autosave
&&
6796 gtk_stext_get_length(GTK_STEXT(widget
)) % prefs_common
.autosave_length
== 0)
6797 compose_draft_cb((gpointer
)compose
, 1, NULL
);
6800 static gboolean
compose_send_control_enter(Compose
*compose
)
6804 GtkItemFactory
*ifactory
;
6805 GtkAccelEntry
*accel
;
6806 GtkWidget
*send_menu
;
6808 GdkModifierType ignored_mods
=
6809 (GDK_LOCK_MASK
| GDK_MOD2_MASK
| GDK_MOD3_MASK
|
6810 GDK_MOD4_MASK
| GDK_MOD5_MASK
);
6812 ev
= gtk_get_current_event();
6813 if (ev
->type
!= GDK_KEY_PRESS
) return FALSE
;
6815 kev
= (GdkEventKey
*)ev
;
6816 if (!(kev
->keyval
== GDK_Return
&& (kev
->state
& GDK_CONTROL_MASK
)))
6819 ifactory
= gtk_item_factory_from_widget(compose
->menubar
);
6820 send_menu
= gtk_item_factory_get_widget(ifactory
, "/Message/Send");
6821 list
= gtk_accel_group_entries_from_object(GTK_OBJECT(send_menu
));
6825 accel
= (GtkAccelEntry
*)list
->data
;
6826 if (accel
&& accel
->accelerator_key
== kev
->keyval
&&
6827 (accel
->accelerator_mods
& ~ignored_mods
) ==
6828 (kev
->state
& ~ignored_mods
)) {
6829 compose_send_cb(compose
, 0, NULL
);
6837 static void compose_check_all(Compose
*compose
)
6839 if (compose
->gtkaspell
)
6840 gtkaspell_check_all(compose
->gtkaspell
);
6843 static void compose_highlight_all(Compose
*compose
)
6845 if (compose
->gtkaspell
)
6846 gtkaspell_highlight_all(compose
->gtkaspell
);
6849 static void compose_check_backwards(Compose
*compose
)
6851 if (compose
->gtkaspell
)
6852 gtkaspell_check_backwards(compose
->gtkaspell
);
6854 GtkItemFactory
*ifactory
;
6855 ifactory
= gtk_item_factory_from_widget(compose
->popupmenu
);
6856 menu_set_sensitive(ifactory
, "/Edit/Check backwards misspelled word", FALSE
);
6857 menu_set_sensitive(ifactory
, "/Edit/Forward to next misspelled word", FALSE
);
6861 static void compose_check_forwards_go(Compose
*compose
)
6863 if (compose
->gtkaspell
)
6864 gtkaspell_check_forwards_go(compose
->gtkaspell
);
6866 GtkItemFactory
*ifactory
;
6867 ifactory
= gtk_item_factory_from_widget(compose
->popupmenu
);
6868 menu_set_sensitive(ifactory
, "/Edit/Check backwards misspelled word", FALSE
);
6869 menu_set_sensitive(ifactory
, "/Edit/Forward to next misspelled word", FALSE
);