2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
167 SUBJECT_FIELD_PRESENT,
172 #define B64_LINE_SIZE 57
173 #define B64_BUFFSIZE 77
175 #define MAX_REFERENCES_LEN 999
177 static GList *compose_list = NULL;
179 static Compose *compose_generic_new (PrefsAccount *account,
182 GPtrArray *attach_files,
183 GList *listAddress );
185 static Compose *compose_create (PrefsAccount *account,
190 static void compose_entry_mark_default_to (Compose *compose,
191 const gchar *address);
192 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
197 static Compose *compose_forward_multiple (PrefsAccount *account,
198 GSList *msginfo_list);
199 static Compose *compose_reply (MsgInfo *msginfo,
200 ComposeQuoteMode quote_mode,
205 static Compose *compose_reply_mode (ComposeMode mode,
206 GSList *msginfo_list,
208 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
209 static void compose_update_privacy_systems_menu(Compose *compose);
211 static GtkWidget *compose_account_option_menu_create
213 static void compose_set_out_encoding (Compose *compose);
214 static void compose_set_template_menu (Compose *compose);
215 static void compose_destroy (Compose *compose);
217 static MailField compose_entries_set (Compose *compose,
219 ComposeEntryType to_type);
220 static gint compose_parse_header (Compose *compose,
222 static gchar *compose_parse_references (const gchar *ref,
225 static gchar *compose_quote_fmt (Compose *compose,
231 gboolean need_unescape,
232 const gchar *err_msg);
234 static void compose_reply_set_entry (Compose *compose,
240 followup_and_reply_to);
241 static void compose_reedit_set_entry (Compose *compose,
244 static void compose_insert_sig (Compose *compose,
246 static ComposeInsertResult compose_insert_file (Compose *compose,
249 static gboolean compose_attach_append (Compose *compose,
252 const gchar *content_type);
253 static void compose_attach_parts (Compose *compose,
256 static gboolean compose_beautify_paragraph (Compose *compose,
257 GtkTextIter *par_iter,
259 static void compose_wrap_all (Compose *compose);
260 static void compose_wrap_all_full (Compose *compose,
263 static void compose_set_title (Compose *compose);
264 static void compose_select_account (Compose *compose,
265 PrefsAccount *account,
268 static PrefsAccount *compose_current_mail_account(void);
269 /* static gint compose_send (Compose *compose); */
270 static gboolean compose_check_for_valid_recipient
272 static gboolean compose_check_entries (Compose *compose,
273 gboolean check_everything);
274 static gint compose_write_to_file (Compose *compose,
277 gboolean attach_parts);
278 static gint compose_write_body_to_file (Compose *compose,
280 static gint compose_remove_reedit_target (Compose *compose,
282 static void compose_remove_draft (Compose *compose);
283 static gint compose_queue_sub (Compose *compose,
287 gboolean check_subject,
288 gboolean remove_reedit_target);
289 static int compose_add_attachments (Compose *compose,
291 static gchar *compose_get_header (Compose *compose);
293 static void compose_convert_header (Compose *compose,
298 gboolean addr_field);
300 static void compose_attach_info_free (AttachInfo *ainfo);
301 static void compose_attach_remove_selected (GtkAction *action,
304 static void compose_template_apply (Compose *compose,
307 static void compose_attach_property (GtkAction *action,
309 static void compose_attach_property_create (gboolean *cancelled);
310 static void attach_property_ok (GtkWidget *widget,
311 gboolean *cancelled);
312 static void attach_property_cancel (GtkWidget *widget,
313 gboolean *cancelled);
314 static gint attach_property_delete_event (GtkWidget *widget,
316 gboolean *cancelled);
317 static gboolean attach_property_key_pressed (GtkWidget *widget,
319 gboolean *cancelled);
321 static void compose_exec_ext_editor (Compose *compose);
323 static gint compose_exec_ext_editor_real (const gchar *file);
324 static gboolean compose_ext_editor_kill (Compose *compose);
325 static gboolean compose_input_cb (GIOChannel *source,
326 GIOCondition condition,
328 static void compose_set_ext_editor_sensitive (Compose *compose,
330 #endif /* G_OS_UNIX */
332 static void compose_undo_state_changed (UndoMain *undostruct,
337 static void compose_create_header_entry (Compose *compose);
338 static void compose_add_header_entry (Compose *compose, const gchar *header,
339 gchar *text, ComposePrefType pref_type);
340 static void compose_remove_header_entries(Compose *compose);
342 static void compose_update_priority_menu_item(Compose * compose);
344 static void compose_spell_menu_changed (void *data);
345 static void compose_dict_changed (void *data);
347 static void compose_add_field_list ( Compose *compose,
348 GList *listAddress );
350 /* callback functions */
352 static gboolean compose_edit_size_alloc (GtkEditable *widget,
353 GtkAllocation *allocation,
354 GtkSHRuler *shruler);
355 static void account_activated (GtkComboBox *optmenu,
357 static void attach_selected (GtkTreeView *tree_view,
358 GtkTreePath *tree_path,
359 GtkTreeViewColumn *column,
361 static gboolean attach_button_pressed (GtkWidget *widget,
362 GdkEventButton *event,
364 static gboolean attach_key_pressed (GtkWidget *widget,
367 static void compose_send_cb (GtkAction *action, gpointer data);
368 static void compose_send_later_cb (GtkAction *action, gpointer data);
370 static void compose_save_cb (GtkAction *action,
373 static void compose_attach_cb (GtkAction *action,
375 static void compose_insert_file_cb (GtkAction *action,
377 static void compose_insert_sig_cb (GtkAction *action,
380 static void compose_close_cb (GtkAction *action,
383 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
385 static void compose_address_cb (GtkAction *action,
387 static void about_show_cb (GtkAction *action,
389 static void compose_template_activate_cb(GtkWidget *widget,
392 static void compose_ext_editor_cb (GtkAction *action,
395 static gint compose_delete_cb (GtkWidget *widget,
399 static void compose_undo_cb (GtkAction *action,
401 static void compose_redo_cb (GtkAction *action,
403 static void compose_cut_cb (GtkAction *action,
405 static void compose_copy_cb (GtkAction *action,
407 static void compose_paste_cb (GtkAction *action,
409 static void compose_paste_as_quote_cb (GtkAction *action,
411 static void compose_paste_no_wrap_cb (GtkAction *action,
413 static void compose_paste_wrap_cb (GtkAction *action,
415 static void compose_allsel_cb (GtkAction *action,
418 static void compose_advanced_action_cb (GtkAction *action,
421 static void compose_grab_focus_cb (GtkWidget *widget,
424 static void compose_changed_cb (GtkTextBuffer *textbuf,
427 static void compose_wrap_cb (GtkAction *action,
429 static void compose_wrap_all_cb (GtkAction *action,
431 static void compose_find_cb (GtkAction *action,
433 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
435 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
438 static void compose_toggle_ruler_cb (GtkToggleAction *action,
440 static void compose_toggle_sign_cb (GtkToggleAction *action,
442 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
444 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
445 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
446 static void activate_privacy_system (Compose *compose,
447 PrefsAccount *account,
449 static void compose_use_signing(Compose *compose, gboolean use_signing);
450 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
451 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
453 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
455 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
456 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
457 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
459 static void compose_attach_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_insert_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
475 static void compose_header_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
484 static gboolean compose_drag_drop (GtkWidget *widget,
485 GdkDragContext *drag_context,
487 guint time, gpointer user_data);
489 static void text_inserted (GtkTextBuffer *buffer,
494 static Compose *compose_generic_reply(MsgInfo *msginfo,
495 ComposeQuoteMode quote_mode,
499 gboolean followup_and_reply_to,
502 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
503 ComposeHeaderEntry *headerentry);
504 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
506 ComposeHeaderEntry *headerentry);
507 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
508 ComposeHeaderEntry *headerentry);
510 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
512 static void compose_allow_user_actions (Compose *compose, gboolean allow);
514 static void compose_nothing_cb (GtkAction *action, gpointer data)
520 static void compose_check_all (GtkAction *action, gpointer data);
521 static void compose_highlight_all (GtkAction *action, gpointer data);
522 static void compose_check_backwards (GtkAction *action, gpointer data);
523 static void compose_check_forwards_go (GtkAction *action, gpointer data);
526 static gint compose_defer_auto_save_draft (Compose *compose);
527 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
529 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
532 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
533 FolderItem *folder_item);
535 static void compose_attach_update_label(Compose *compose);
536 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
537 gboolean respect_default_to);
539 static GtkActionEntry compose_popup_entries[] =
541 {"Compose", NULL, "Compose" },
542 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
543 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
544 {"Compose/---", NULL, "---", NULL, NULL, NULL },
545 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
548 static GtkActionEntry compose_entries[] =
550 {"Menu", NULL, "Menu" },
552 {"Message", NULL, N_("_Message") },
553 {"Edit", NULL, N_("_Edit") },
555 {"Spelling", NULL, N_("_Spelling") },
557 {"Options", NULL, N_("_Options") },
558 {"Tools", NULL, N_("_Tools") },
559 {"Help", NULL, N_("_Help") },
561 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
562 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
563 {"Message/---", NULL, "---" },
565 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
566 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
567 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
568 /* {"Message/---", NULL, "---" }, */
569 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
570 /* {"Message/---", NULL, "---" }, */
571 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
574 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
575 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
576 {"Edit/---", NULL, "---" },
578 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
579 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
580 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
582 {"Edit/SpecialPaste", NULL, N_("Special paste") },
583 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
584 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
585 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
587 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
589 {"Edit/Advanced", NULL, N_("A_dvanced") },
590 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
591 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
592 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
593 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
594 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
595 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
596 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
597 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
598 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
599 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
600 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
601 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
602 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
603 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
605 /* {"Edit/---", NULL, "---" }, */
606 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
608 /* {"Edit/---", NULL, "---" }, */
609 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
610 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
611 /* {"Edit/---", NULL, "---" }, */
612 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
615 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
616 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
617 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
618 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
620 {"Spelling/---", NULL, "---" },
621 {"Spelling/Options", NULL, N_("_Options") },
626 {"Options/ReplyMode", NULL, N_("Reply _mode") },
627 {"Options/---", NULL, "---" },
628 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
629 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
631 /* {"Options/---", NULL, "---" }, */
633 {"Options/Priority", NULL, N_("_Priority") },
635 {"Options/Encoding", NULL, N_("Character _encoding") },
636 {"Options/Encoding/---", NULL, "---" },
637 #define ENC_ACTION(cs_char,c_char,string) \
638 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
640 {"Options/Encoding/Western", NULL, N_("Western European") },
641 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
642 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
643 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
644 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
645 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
646 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
647 {"Options/Encoding/Korean", NULL, N_("Korean") },
648 {"Options/Encoding/Thai", NULL, N_("Thai") },
651 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
653 {"Tools/Template", NULL, N_("_Template") },
654 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
655 {"Tools/Actions", NULL, N_("Actio_ns") },
656 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
659 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
662 static GtkToggleActionEntry compose_toggle_entries[] =
664 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
665 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
666 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
667 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
668 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
669 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
670 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
673 static GtkRadioActionEntry compose_radio_rm_entries[] =
675 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
676 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
677 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
681 static GtkRadioActionEntry compose_radio_prio_entries[] =
683 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
684 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
690 static GtkRadioActionEntry compose_radio_enc_entries[] =
692 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
726 static GtkTargetEntry compose_mime_types[] =
728 {"text/uri-list", 0, 0},
729 {"UTF8_STRING", 0, 0},
733 static gboolean compose_put_existing_to_front(MsgInfo *info)
735 GList *compose_list = compose_get_compose_list();
739 for (elem = compose_list; elem != NULL && elem->data != NULL;
741 Compose *c = (Compose*)elem->data;
743 if (!c->targetinfo || !c->targetinfo->msgid ||
747 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
748 gtkut_window_popup(c->window);
756 static GdkColor quote_color1 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_color2 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_color3 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
763 static GdkColor quote_bgcolor1 =
764 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor2 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor3 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor signature_color = {
777 static GdkColor uri_color = {
784 static void compose_create_tags(GtkTextView *text, Compose *compose)
786 GtkTextBuffer *buffer;
787 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 buffer = gtk_text_view_get_buffer(text);
795 if (prefs_common.enable_color) {
796 /* grab the quote colors, converting from an int to a GdkColor */
797 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
799 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
807 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
809 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
811 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
814 signature_color = quote_color1 = quote_color2 = quote_color3 =
815 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
818 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
819 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820 "foreground-gdk", "e_color1,
821 "paragraph-background-gdk", "e_bgcolor1,
823 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
824 "foreground-gdk", "e_color2,
825 "paragraph-background-gdk", "e_bgcolor2,
827 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
828 "foreground-gdk", "e_color3,
829 "paragraph-background-gdk", "e_bgcolor3,
832 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
833 "foreground-gdk", "e_color1,
835 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
836 "foreground-gdk", "e_color2,
838 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
839 "foreground-gdk", "e_color3,
843 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
844 "foreground-gdk", &signature_color,
847 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
848 "foreground-gdk", &uri_color,
850 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
851 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
853 color[0] = quote_color1;
854 color[1] = quote_color2;
855 color[2] = quote_color3;
856 color[3] = quote_bgcolor1;
857 color[4] = quote_bgcolor2;
858 color[5] = quote_bgcolor3;
859 color[6] = signature_color;
860 color[7] = uri_color;
861 cmap = gdk_drawable_get_colormap(compose->window->window);
862 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
864 for (i = 0; i < 8; i++) {
865 if (success[i] == FALSE) {
868 g_warning("Compose: color allocation failed.\n");
869 style = gtk_widget_get_style(GTK_WIDGET(text));
870 quote_color1 = quote_color2 = quote_color3 =
871 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
872 signature_color = uri_color = black;
877 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
878 GPtrArray *attach_files)
880 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
883 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
885 return compose_generic_new(account, mailto, item, NULL, NULL);
888 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
890 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
893 #define SCROLL_TO_CURSOR(compose) { \
894 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
895 gtk_text_view_get_buffer( \
896 GTK_TEXT_VIEW(compose->text))); \
897 gtk_text_view_scroll_mark_onscreen( \
898 GTK_TEXT_VIEW(compose->text), \
902 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
905 if (folderidentifier) {
906 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
907 prefs_common.compose_save_to_history = add_history(
908 prefs_common.compose_save_to_history, folderidentifier);
909 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
910 prefs_common.compose_save_to_history);
913 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
914 if (folderidentifier)
915 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
917 gtk_entry_set_text(GTK_ENTRY(entry), "");
920 static gchar *compose_get_save_to(Compose *compose)
923 gchar *result = NULL;
924 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
925 result = gtk_editable_get_chars(entry, 0, -1);
928 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
929 prefs_common.compose_save_to_history = add_history(
930 prefs_common.compose_save_to_history, result);
931 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
932 prefs_common.compose_save_to_history);
937 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
938 GPtrArray *attach_files, GList *listAddress )
941 GtkTextView *textview;
942 GtkTextBuffer *textbuf;
944 const gchar *subject_format = NULL;
945 const gchar *body_format = NULL;
946 gchar *mailto_from = NULL;
947 PrefsAccount *mailto_account = NULL;
948 MsgInfo* dummyinfo = NULL;
949 MailField mfield = NO_FIELD_PRESENT;
953 /* check if mailto defines a from */
954 if (mailto && *mailto != '\0') {
955 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
956 /* mailto defines a from, check if we can get account prefs from it,
957 if not, the account prefs will be guessed using other ways, but we'll keep
960 mailto_account = account_find_from_address(mailto_from, TRUE);
962 account = mailto_account;
965 /* if no account prefs set from mailto, set if from folder prefs (if any) */
966 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
967 account = account_find_from_id(item->prefs->default_account);
969 /* if no account prefs set, fallback to the current one */
970 if (!account) account = cur_account;
971 cm_return_val_if_fail(account != NULL, NULL);
973 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
975 /* override from name if mailto asked for it */
977 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
980 /* override from name according to folder properties */
981 if (item && item->prefs &&
982 item->prefs->compose_with_format &&
983 item->prefs->compose_override_from_format &&
984 *item->prefs->compose_override_from_format != '\0') {
989 dummyinfo = compose_msginfo_new_from_compose(compose);
991 /* decode \-escape sequences in the internal representation of the quote format */
992 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
993 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
996 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
999 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1001 quote_fmt_scan_string(tmp);
1004 buf = quote_fmt_get_buffer();
1006 alertpanel_error(_("New message From format error."));
1008 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1009 quote_fmt_reset_vartable();
1014 compose->replyinfo = NULL;
1015 compose->fwdinfo = NULL;
1017 textview = GTK_TEXT_VIEW(compose->text);
1018 textbuf = gtk_text_view_get_buffer(textview);
1019 compose_create_tags(textview, compose);
1021 undo_block(compose->undostruct);
1023 compose_set_dictionaries_from_folder_prefs(compose, item);
1026 if (account->auto_sig)
1027 compose_insert_sig(compose, FALSE);
1028 gtk_text_buffer_get_start_iter(textbuf, &iter);
1029 gtk_text_buffer_place_cursor(textbuf, &iter);
1031 if (account->protocol != A_NNTP) {
1032 if (mailto && *mailto != '\0') {
1033 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1036 compose_set_folder_prefs(compose, item, TRUE);
1038 if (item && item->ret_rcpt) {
1039 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1042 if (mailto && *mailto != '\0') {
1043 if (!strchr(mailto, '@'))
1044 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1046 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1047 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1048 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1049 mfield = TO_FIELD_PRESENT;
1052 * CLAWS: just don't allow return receipt request, even if the user
1053 * may want to send an email. simple but foolproof.
1055 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1057 compose_add_field_list( compose, listAddress );
1059 if (item && item->prefs && item->prefs->compose_with_format) {
1060 subject_format = item->prefs->compose_subject_format;
1061 body_format = item->prefs->compose_body_format;
1062 } else if (account->compose_with_format) {
1063 subject_format = account->compose_subject_format;
1064 body_format = account->compose_body_format;
1065 } else if (prefs_common.compose_with_format) {
1066 subject_format = prefs_common.compose_subject_format;
1067 body_format = prefs_common.compose_body_format;
1070 if (subject_format || body_format) {
1073 && *subject_format != '\0' )
1075 gchar *subject = NULL;
1080 dummyinfo = compose_msginfo_new_from_compose(compose);
1082 /* decode \-escape sequences in the internal representation of the quote format */
1083 tmp = malloc(strlen(subject_format)+1);
1084 pref_get_unescaped_pref(tmp, subject_format);
1086 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1089 compose->gtkaspell);
1091 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1093 quote_fmt_scan_string(tmp);
1096 buf = quote_fmt_get_buffer();
1098 alertpanel_error(_("New message subject format error."));
1100 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1101 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1102 quote_fmt_reset_vartable();
1106 mfield = SUBJECT_FIELD_PRESENT;
1110 && *body_format != '\0' )
1113 GtkTextBuffer *buffer;
1114 GtkTextIter start, end;
1118 dummyinfo = compose_msginfo_new_from_compose(compose);
1120 text = GTK_TEXT_VIEW(compose->text);
1121 buffer = gtk_text_view_get_buffer(text);
1122 gtk_text_buffer_get_start_iter(buffer, &start);
1123 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1124 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1126 compose_quote_fmt(compose, dummyinfo,
1128 NULL, tmp, FALSE, TRUE,
1129 _("The body of the \"New message\" template has an error at line %d."));
1130 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1131 quote_fmt_reset_vartable();
1135 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1136 gtkaspell_highlight_all(compose->gtkaspell);
1138 mfield = BODY_FIELD_PRESENT;
1142 procmsg_msginfo_free( dummyinfo );
1148 for (i = 0; i < attach_files->len; i++) {
1149 file = g_ptr_array_index(attach_files, i);
1150 compose_attach_append(compose, file, file, NULL);
1154 compose_show_first_last_header(compose, TRUE);
1156 /* Set save folder */
1157 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1158 gchar *folderidentifier;
1160 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1161 folderidentifier = folder_item_get_identifier(item);
1162 compose_set_save_to(compose, folderidentifier);
1163 g_free(folderidentifier);
1166 /* Place cursor according to provided input (mfield) */
1168 case NO_FIELD_PRESENT:
1169 if (compose->header_last)
1170 gtk_widget_grab_focus(compose->header_last->entry);
1172 case TO_FIELD_PRESENT:
1173 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1175 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1178 gtk_widget_grab_focus(compose->subject_entry);
1180 case SUBJECT_FIELD_PRESENT:
1181 textview = GTK_TEXT_VIEW(compose->text);
1184 textbuf = gtk_text_view_get_buffer(textview);
1187 mark = gtk_text_buffer_get_insert(textbuf);
1188 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1189 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1191 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1192 * only defers where it comes to the variable body
1193 * is not null. If no body is present compose->text
1194 * will be null in which case you cannot place the
1195 * cursor inside the component so. An empty component
1196 * is therefore created before placing the cursor
1198 case BODY_FIELD_PRESENT:
1199 gtk_widget_grab_focus(compose->text);
1203 undo_unblock(compose->undostruct);
1205 if (prefs_common.auto_exteditor)
1206 compose_exec_ext_editor(compose);
1208 compose->draft_timeout_tag = -1;
1209 SCROLL_TO_CURSOR(compose);
1211 compose->modified = FALSE;
1212 compose_set_title(compose);
1214 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1219 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1220 gboolean override_pref, const gchar *system)
1222 const gchar *privacy = NULL;
1224 cm_return_if_fail(compose != NULL);
1225 cm_return_if_fail(account != NULL);
1227 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1232 else if (account->default_privacy_system
1233 && strlen(account->default_privacy_system)) {
1234 privacy = account->default_privacy_system;
1236 GSList *privacy_avail = privacy_get_system_ids();
1237 if (privacy_avail && g_slist_length(privacy_avail)) {
1238 privacy = (gchar *)(privacy_avail->data);
1241 if (privacy != NULL) {
1243 g_free(compose->privacy_system);
1244 compose->privacy_system = NULL;
1246 if (compose->privacy_system == NULL)
1247 compose->privacy_system = g_strdup(privacy);
1248 else if (*(compose->privacy_system) == '\0') {
1249 g_free(compose->privacy_system);
1250 compose->privacy_system = g_strdup(privacy);
1252 compose_update_privacy_system_menu_item(compose, FALSE);
1253 compose_use_encryption(compose, TRUE);
1257 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1259 const gchar *privacy = NULL;
1263 else if (account->default_privacy_system
1264 && strlen(account->default_privacy_system)) {
1265 privacy = account->default_privacy_system;
1267 GSList *privacy_avail = privacy_get_system_ids();
1268 if (privacy_avail && g_slist_length(privacy_avail)) {
1269 privacy = (gchar *)(privacy_avail->data);
1273 if (privacy != NULL) {
1275 g_free(compose->privacy_system);
1276 compose->privacy_system = NULL;
1278 if (compose->privacy_system == NULL)
1279 compose->privacy_system = g_strdup(privacy);
1280 compose_update_privacy_system_menu_item(compose, FALSE);
1281 compose_use_signing(compose, TRUE);
1285 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1289 Compose *compose = NULL;
1291 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1293 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1294 cm_return_val_if_fail(msginfo != NULL, NULL);
1296 list_len = g_slist_length(msginfo_list);
1300 case COMPOSE_REPLY_TO_ADDRESS:
1301 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1302 FALSE, prefs_common.default_reply_list, FALSE, body);
1304 case COMPOSE_REPLY_WITH_QUOTE:
1305 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1306 FALSE, prefs_common.default_reply_list, FALSE, body);
1308 case COMPOSE_REPLY_WITHOUT_QUOTE:
1309 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1310 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1312 case COMPOSE_REPLY_TO_SENDER:
1313 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1314 FALSE, FALSE, TRUE, body);
1316 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1317 compose = compose_followup_and_reply_to(msginfo,
1318 COMPOSE_QUOTE_CHECK,
1319 FALSE, FALSE, body);
1321 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1322 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1323 FALSE, FALSE, TRUE, body);
1325 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1326 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1327 FALSE, FALSE, TRUE, NULL);
1329 case COMPOSE_REPLY_TO_ALL:
1330 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1331 TRUE, FALSE, FALSE, body);
1333 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1335 TRUE, FALSE, FALSE, body);
1337 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1338 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1339 TRUE, FALSE, FALSE, NULL);
1341 case COMPOSE_REPLY_TO_LIST:
1342 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1343 FALSE, TRUE, FALSE, body);
1345 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1346 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1347 FALSE, TRUE, FALSE, body);
1349 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1350 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1351 FALSE, TRUE, FALSE, NULL);
1353 case COMPOSE_FORWARD:
1354 if (prefs_common.forward_as_attachment) {
1355 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1358 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1362 case COMPOSE_FORWARD_INLINE:
1363 /* check if we reply to more than one Message */
1364 if (list_len == 1) {
1365 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1368 /* more messages FALL THROUGH */
1369 case COMPOSE_FORWARD_AS_ATTACH:
1370 compose = compose_forward_multiple(NULL, msginfo_list);
1372 case COMPOSE_REDIRECT:
1373 compose = compose_redirect(NULL, msginfo, FALSE);
1376 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1379 if (compose == NULL) {
1380 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1384 compose->rmode = mode;
1385 switch (compose->rmode) {
1387 case COMPOSE_REPLY_WITH_QUOTE:
1388 case COMPOSE_REPLY_WITHOUT_QUOTE:
1389 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1390 debug_print("reply mode Normal\n");
1391 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1392 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1394 case COMPOSE_REPLY_TO_SENDER:
1395 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1396 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1397 debug_print("reply mode Sender\n");
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1400 case COMPOSE_REPLY_TO_ALL:
1401 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1402 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1403 debug_print("reply mode All\n");
1404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1406 case COMPOSE_REPLY_TO_LIST:
1407 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1408 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1409 debug_print("reply mode List\n");
1410 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1412 case COMPOSE_REPLY_TO_ADDRESS:
1413 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1421 static Compose *compose_reply(MsgInfo *msginfo,
1422 ComposeQuoteMode quote_mode,
1428 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1429 to_sender, FALSE, body);
1432 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1433 ComposeQuoteMode quote_mode,
1438 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1439 to_sender, TRUE, body);
1442 static void compose_extract_original_charset(Compose *compose)
1444 MsgInfo *info = NULL;
1445 if (compose->replyinfo) {
1446 info = compose->replyinfo;
1447 } else if (compose->fwdinfo) {
1448 info = compose->fwdinfo;
1449 } else if (compose->targetinfo) {
1450 info = compose->targetinfo;
1453 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1454 MimeInfo *partinfo = mimeinfo;
1455 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1456 partinfo = procmime_mimeinfo_next(partinfo);
1458 compose->orig_charset =
1459 g_strdup(procmime_mimeinfo_get_parameter(
1460 partinfo, "charset"));
1462 procmime_mimeinfo_free_all(mimeinfo);
1466 #define SIGNAL_BLOCK(buffer) { \
1467 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1468 G_CALLBACK(compose_changed_cb), \
1470 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1471 G_CALLBACK(text_inserted), \
1475 #define SIGNAL_UNBLOCK(buffer) { \
1476 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1477 G_CALLBACK(compose_changed_cb), \
1479 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1480 G_CALLBACK(text_inserted), \
1484 static Compose *compose_generic_reply(MsgInfo *msginfo,
1485 ComposeQuoteMode quote_mode,
1486 gboolean to_all, gboolean to_ml,
1488 gboolean followup_and_reply_to,
1492 PrefsAccount *account = NULL;
1493 GtkTextView *textview;
1494 GtkTextBuffer *textbuf;
1495 gboolean quote = FALSE;
1496 const gchar *qmark = NULL;
1497 const gchar *body_fmt = NULL;
1498 gchar *s_system = NULL;
1500 cm_return_val_if_fail(msginfo != NULL, NULL);
1501 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1503 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1505 cm_return_val_if_fail(account != NULL, NULL);
1507 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1509 compose->updating = TRUE;
1511 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1512 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1514 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1515 if (!compose->replyinfo)
1516 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1518 compose_extract_original_charset(compose);
1520 if (msginfo->folder && msginfo->folder->ret_rcpt)
1521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1523 /* Set save folder */
1524 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1525 gchar *folderidentifier;
1527 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1528 folderidentifier = folder_item_get_identifier(msginfo->folder);
1529 compose_set_save_to(compose, folderidentifier);
1530 g_free(folderidentifier);
1533 if (compose_parse_header(compose, msginfo) < 0) {
1534 compose->updating = FALSE;
1535 compose_destroy(compose);
1539 /* override from name according to folder properties */
1540 if (msginfo->folder && msginfo->folder->prefs &&
1541 msginfo->folder->prefs->reply_with_format &&
1542 msginfo->folder->prefs->reply_override_from_format &&
1543 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1548 /* decode \-escape sequences in the internal representation of the quote format */
1549 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1550 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1553 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1554 compose->gtkaspell);
1556 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1558 quote_fmt_scan_string(tmp);
1561 buf = quote_fmt_get_buffer();
1563 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1565 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1566 quote_fmt_reset_vartable();
1571 textview = (GTK_TEXT_VIEW(compose->text));
1572 textbuf = gtk_text_view_get_buffer(textview);
1573 compose_create_tags(textview, compose);
1575 undo_block(compose->undostruct);
1577 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1580 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1581 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1582 /* use the reply format of folder (if enabled), or the account's one
1583 (if enabled) or fallback to the global reply format, which is always
1584 enabled (even if empty), and use the relevant quotemark */
1586 if (msginfo->folder && msginfo->folder->prefs &&
1587 msginfo->folder->prefs->reply_with_format) {
1588 qmark = msginfo->folder->prefs->reply_quotemark;
1589 body_fmt = msginfo->folder->prefs->reply_body_format;
1591 } else if (account->reply_with_format) {
1592 qmark = account->reply_quotemark;
1593 body_fmt = account->reply_body_format;
1596 qmark = prefs_common.quotemark;
1597 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1598 body_fmt = gettext(prefs_common.quotefmt);
1605 /* empty quotemark is not allowed */
1606 if (qmark == NULL || *qmark == '\0')
1608 compose_quote_fmt(compose, compose->replyinfo,
1609 body_fmt, qmark, body, FALSE, TRUE,
1610 _("The body of the \"Reply\" template has an error at line %d."));
1611 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1612 quote_fmt_reset_vartable();
1614 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1615 gtkaspell_highlight_all(compose->gtkaspell);
1619 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1620 compose_force_encryption(compose, account, FALSE, s_system);
1623 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1624 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1625 compose_force_signing(compose, account, s_system);
1629 SIGNAL_BLOCK(textbuf);
1631 if (account->auto_sig)
1632 compose_insert_sig(compose, FALSE);
1634 compose_wrap_all(compose);
1636 SIGNAL_UNBLOCK(textbuf);
1638 gtk_widget_grab_focus(compose->text);
1640 undo_unblock(compose->undostruct);
1642 if (prefs_common.auto_exteditor)
1643 compose_exec_ext_editor(compose);
1645 compose->modified = FALSE;
1646 compose_set_title(compose);
1648 compose->updating = FALSE;
1649 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1650 SCROLL_TO_CURSOR(compose);
1652 if (compose->deferred_destroy) {
1653 compose_destroy(compose);
1661 #define INSERT_FW_HEADER(var, hdr) \
1662 if (msginfo->var && *msginfo->var) { \
1663 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1664 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1665 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1668 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1669 gboolean as_attach, const gchar *body,
1670 gboolean no_extedit,
1674 GtkTextView *textview;
1675 GtkTextBuffer *textbuf;
1678 cm_return_val_if_fail(msginfo != NULL, NULL);
1679 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1682 !(account = compose_guess_forward_account_from_msginfo
1684 account = cur_account;
1686 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1688 compose->updating = TRUE;
1689 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1690 if (!compose->fwdinfo)
1691 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1693 compose_extract_original_charset(compose);
1695 if (msginfo->subject && *msginfo->subject) {
1696 gchar *buf, *buf2, *p;
1698 buf = p = g_strdup(msginfo->subject);
1699 p += subject_get_prefix_length(p);
1700 memmove(buf, p, strlen(p) + 1);
1702 buf2 = g_strdup_printf("Fw: %s", buf);
1703 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1709 /* override from name according to folder properties */
1710 if (msginfo->folder && msginfo->folder->prefs &&
1711 msginfo->folder->prefs->forward_with_format &&
1712 msginfo->folder->prefs->forward_override_from_format &&
1713 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1717 MsgInfo *full_msginfo = NULL;
1720 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1722 full_msginfo = procmsg_msginfo_copy(msginfo);
1724 /* decode \-escape sequences in the internal representation of the quote format */
1725 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1726 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1729 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1730 compose->gtkaspell);
1732 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1734 quote_fmt_scan_string(tmp);
1737 buf = quote_fmt_get_buffer();
1739 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1741 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1742 quote_fmt_reset_vartable();
1745 procmsg_msginfo_free(full_msginfo);
1748 textview = GTK_TEXT_VIEW(compose->text);
1749 textbuf = gtk_text_view_get_buffer(textview);
1750 compose_create_tags(textview, compose);
1752 undo_block(compose->undostruct);
1756 msgfile = procmsg_get_message_file(msginfo);
1757 if (!is_file_exist(msgfile))
1758 g_warning("%s: file not exist\n", msgfile);
1760 compose_attach_append(compose, msgfile, msgfile,
1765 const gchar *qmark = NULL;
1766 const gchar *body_fmt = NULL;
1767 MsgInfo *full_msginfo;
1769 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1770 body_fmt = gettext(prefs_common.fw_quotefmt);
1774 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1776 full_msginfo = procmsg_msginfo_copy(msginfo);
1778 /* use the forward format of folder (if enabled), or the account's one
1779 (if enabled) or fallback to the global forward format, which is always
1780 enabled (even if empty), and use the relevant quotemark */
1781 if (msginfo->folder && msginfo->folder->prefs &&
1782 msginfo->folder->prefs->forward_with_format) {
1783 qmark = msginfo->folder->prefs->forward_quotemark;
1784 body_fmt = msginfo->folder->prefs->forward_body_format;
1786 } else if (account->forward_with_format) {
1787 qmark = account->forward_quotemark;
1788 body_fmt = account->forward_body_format;
1791 qmark = prefs_common.fw_quotemark;
1792 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1793 body_fmt = gettext(prefs_common.fw_quotefmt);
1798 /* empty quotemark is not allowed */
1799 if (qmark == NULL || *qmark == '\0')
1802 compose_quote_fmt(compose, full_msginfo,
1803 body_fmt, qmark, body, FALSE, TRUE,
1804 _("The body of the \"Forward\" template has an error at line %d."));
1805 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1806 quote_fmt_reset_vartable();
1807 compose_attach_parts(compose, msginfo);
1809 procmsg_msginfo_free(full_msginfo);
1811 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1812 gtkaspell_highlight_all(compose->gtkaspell);
1816 SIGNAL_BLOCK(textbuf);
1818 if (account->auto_sig)
1819 compose_insert_sig(compose, FALSE);
1821 compose_wrap_all(compose);
1823 SIGNAL_UNBLOCK(textbuf);
1825 gtk_text_buffer_get_start_iter(textbuf, &iter);
1826 gtk_text_buffer_place_cursor(textbuf, &iter);
1828 gtk_widget_grab_focus(compose->header_last->entry);
1830 if (!no_extedit && prefs_common.auto_exteditor)
1831 compose_exec_ext_editor(compose);
1834 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1835 gchar *folderidentifier;
1837 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1838 folderidentifier = folder_item_get_identifier(msginfo->folder);
1839 compose_set_save_to(compose, folderidentifier);
1840 g_free(folderidentifier);
1843 undo_unblock(compose->undostruct);
1845 compose->modified = FALSE;
1846 compose_set_title(compose);
1848 compose->updating = FALSE;
1849 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1850 SCROLL_TO_CURSOR(compose);
1852 if (compose->deferred_destroy) {
1853 compose_destroy(compose);
1857 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1862 #undef INSERT_FW_HEADER
1864 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1867 GtkTextView *textview;
1868 GtkTextBuffer *textbuf;
1872 gboolean single_mail = TRUE;
1874 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1876 if (g_slist_length(msginfo_list) > 1)
1877 single_mail = FALSE;
1879 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1880 if (((MsgInfo *)msginfo->data)->folder == NULL)
1883 /* guess account from first selected message */
1885 !(account = compose_guess_forward_account_from_msginfo
1886 (msginfo_list->data)))
1887 account = cur_account;
1889 cm_return_val_if_fail(account != NULL, NULL);
1891 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1892 if (msginfo->data) {
1893 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1894 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1898 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1899 g_warning("no msginfo_list");
1903 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1905 compose->updating = TRUE;
1907 /* override from name according to folder properties */
1908 if (msginfo_list->data) {
1909 MsgInfo *msginfo = msginfo_list->data;
1911 if (msginfo->folder && msginfo->folder->prefs &&
1912 msginfo->folder->prefs->forward_with_format &&
1913 msginfo->folder->prefs->forward_override_from_format &&
1914 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1919 /* decode \-escape sequences in the internal representation of the quote format */
1920 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1921 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1924 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1925 compose->gtkaspell);
1927 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1929 quote_fmt_scan_string(tmp);
1932 buf = quote_fmt_get_buffer();
1934 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1936 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1937 quote_fmt_reset_vartable();
1943 textview = GTK_TEXT_VIEW(compose->text);
1944 textbuf = gtk_text_view_get_buffer(textview);
1945 compose_create_tags(textview, compose);
1947 undo_block(compose->undostruct);
1948 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1949 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1951 if (!is_file_exist(msgfile))
1952 g_warning("%s: file not exist\n", msgfile);
1954 compose_attach_append(compose, msgfile, msgfile,
1960 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1961 if (info->subject && *info->subject) {
1962 gchar *buf, *buf2, *p;
1964 buf = p = g_strdup(info->subject);
1965 p += subject_get_prefix_length(p);
1966 memmove(buf, p, strlen(p) + 1);
1968 buf2 = g_strdup_printf("Fw: %s", buf);
1969 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1975 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1976 _("Fw: multiple emails"));
1979 SIGNAL_BLOCK(textbuf);
1981 if (account->auto_sig)
1982 compose_insert_sig(compose, FALSE);
1984 compose_wrap_all(compose);
1986 SIGNAL_UNBLOCK(textbuf);
1988 gtk_text_buffer_get_start_iter(textbuf, &iter);
1989 gtk_text_buffer_place_cursor(textbuf, &iter);
1991 gtk_widget_grab_focus(compose->header_last->entry);
1992 undo_unblock(compose->undostruct);
1993 compose->modified = FALSE;
1994 compose_set_title(compose);
1996 compose->updating = FALSE;
1997 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1998 SCROLL_TO_CURSOR(compose);
2000 if (compose->deferred_destroy) {
2001 compose_destroy(compose);
2005 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2010 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2012 GtkTextIter start = *iter;
2013 GtkTextIter end_iter;
2014 int start_pos = gtk_text_iter_get_offset(&start);
2016 if (!compose->account->sig_sep)
2019 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2020 start_pos+strlen(compose->account->sig_sep));
2022 /* check sig separator */
2023 str = gtk_text_iter_get_text(&start, &end_iter);
2024 if (!strcmp(str, compose->account->sig_sep)) {
2026 /* check end of line (\n) */
2027 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2028 start_pos+strlen(compose->account->sig_sep));
2029 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2030 start_pos+strlen(compose->account->sig_sep)+1);
2031 tmp = gtk_text_iter_get_text(&start, &end_iter);
2032 if (!strcmp(tmp,"\n")) {
2044 static void compose_colorize_signature(Compose *compose)
2046 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2048 GtkTextIter end_iter;
2049 gtk_text_buffer_get_start_iter(buffer, &iter);
2050 while (gtk_text_iter_forward_line(&iter))
2051 if (compose_is_sig_separator(compose, buffer, &iter)) {
2052 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2053 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2057 #define BLOCK_WRAP() { \
2058 prev_autowrap = compose->autowrap; \
2059 buffer = gtk_text_view_get_buffer( \
2060 GTK_TEXT_VIEW(compose->text)); \
2061 compose->autowrap = FALSE; \
2063 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2064 G_CALLBACK(compose_changed_cb), \
2066 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2067 G_CALLBACK(text_inserted), \
2070 #define UNBLOCK_WRAP() { \
2071 compose->autowrap = prev_autowrap; \
2072 if (compose->autowrap) { \
2073 gint old = compose->draft_timeout_tag; \
2074 compose->draft_timeout_tag = -2; \
2075 compose_wrap_all(compose); \
2076 compose->draft_timeout_tag = old; \
2079 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2080 G_CALLBACK(compose_changed_cb), \
2082 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2083 G_CALLBACK(text_inserted), \
2087 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2089 Compose *compose = NULL;
2090 PrefsAccount *account = NULL;
2091 GtkTextView *textview;
2092 GtkTextBuffer *textbuf;
2096 gchar buf[BUFFSIZE];
2097 gboolean use_signing = FALSE;
2098 gboolean use_encryption = FALSE;
2099 gchar *privacy_system = NULL;
2100 int priority = PRIORITY_NORMAL;
2101 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2102 gboolean autowrap = prefs_common.autowrap;
2103 gboolean autoindent = prefs_common.auto_indent;
2105 cm_return_val_if_fail(msginfo != NULL, NULL);
2106 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2108 if (compose_put_existing_to_front(msginfo)) {
2112 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2113 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2114 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2115 gchar queueheader_buf[BUFFSIZE];
2118 /* Select Account from queue headers */
2119 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2120 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2121 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2122 account = account_find_from_id(id);
2124 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2125 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2126 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2127 account = account_find_from_id(id);
2129 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2130 sizeof(queueheader_buf), "NAID:")) {
2131 id = atoi(&queueheader_buf[strlen("NAID:")]);
2132 account = account_find_from_id(id);
2134 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2135 sizeof(queueheader_buf), "MAID:")) {
2136 id = atoi(&queueheader_buf[strlen("MAID:")]);
2137 account = account_find_from_id(id);
2139 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2140 sizeof(queueheader_buf), "S:")) {
2141 account = account_find_from_address(queueheader_buf, FALSE);
2143 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2144 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2145 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2146 use_signing = param;
2149 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2150 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2151 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2152 use_signing = param;
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2157 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2158 use_encryption = param;
2160 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2161 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2162 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2163 use_encryption = param;
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2167 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2170 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2171 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2172 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2175 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2177 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2181 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2183 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2184 sizeof(queueheader_buf), "X-Priority: ")) {
2185 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "RMID:")) {
2190 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2191 if (tokens[0] && tokens[1] && tokens[2]) {
2192 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2193 if (orig_item != NULL) {
2194 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "FMID:")) {
2201 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2202 if (tokens[0] && tokens[1] && tokens[2]) {
2203 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2204 if (orig_item != NULL) {
2205 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2211 account = msginfo->folder->folder->account;
2214 if (!account && prefs_common.reedit_account_autosel) {
2215 gchar from[BUFFSIZE];
2216 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2217 extract_address(from);
2218 account = account_find_from_address(from, FALSE);
2222 account = cur_account;
2224 cm_return_val_if_fail(account != NULL, NULL);
2226 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2228 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2229 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2230 compose->autowrap = autowrap;
2231 compose->replyinfo = replyinfo;
2232 compose->fwdinfo = fwdinfo;
2234 compose->updating = TRUE;
2235 compose->priority = priority;
2237 if (privacy_system != NULL) {
2238 compose->privacy_system = privacy_system;
2239 compose_use_signing(compose, use_signing);
2240 compose_use_encryption(compose, use_encryption);
2241 compose_update_privacy_system_menu_item(compose, FALSE);
2243 activate_privacy_system(compose, account, FALSE);
2246 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2248 compose_extract_original_charset(compose);
2250 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2251 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2252 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2253 gchar queueheader_buf[BUFFSIZE];
2255 /* Set message save folder */
2256 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2257 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2258 compose_set_save_to(compose, &queueheader_buf[4]);
2260 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2261 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2263 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2268 if (compose_parse_header(compose, msginfo) < 0) {
2269 compose->updating = FALSE;
2270 compose_destroy(compose);
2273 compose_reedit_set_entry(compose, msginfo);
2275 textview = GTK_TEXT_VIEW(compose->text);
2276 textbuf = gtk_text_view_get_buffer(textview);
2277 compose_create_tags(textview, compose);
2279 mark = gtk_text_buffer_get_insert(textbuf);
2280 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2282 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2283 G_CALLBACK(compose_changed_cb),
2286 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2287 fp = procmime_get_first_encrypted_text_content(msginfo);
2289 compose_force_encryption(compose, account, TRUE, NULL);
2292 fp = procmime_get_first_text_content(msginfo);
2295 g_warning("Can't get text part\n");
2299 gboolean prev_autowrap = compose->autowrap;
2300 GtkTextBuffer *buffer = textbuf;
2302 while (fgets(buf, sizeof(buf), fp) != NULL) {
2304 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2310 compose_attach_parts(compose, msginfo);
2312 compose_colorize_signature(compose);
2314 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2315 G_CALLBACK(compose_changed_cb),
2318 gtk_widget_grab_focus(compose->text);
2320 if (prefs_common.auto_exteditor) {
2321 compose_exec_ext_editor(compose);
2323 compose->modified = FALSE;
2324 compose_set_title(compose);
2326 compose->updating = FALSE;
2327 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2328 SCROLL_TO_CURSOR(compose);
2330 if (compose->deferred_destroy) {
2331 compose_destroy(compose);
2335 compose->sig_str = account_get_signature_str(compose->account);
2337 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2342 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2349 cm_return_val_if_fail(msginfo != NULL, NULL);
2352 account = account_get_reply_account(msginfo,
2353 prefs_common.reply_account_autosel);
2354 cm_return_val_if_fail(account != NULL, NULL);
2356 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2358 compose->updating = TRUE;
2360 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2361 compose->replyinfo = NULL;
2362 compose->fwdinfo = NULL;
2364 compose_show_first_last_header(compose, TRUE);
2366 gtk_widget_grab_focus(compose->header_last->entry);
2368 filename = procmsg_get_message_file(msginfo);
2370 if (filename == NULL) {
2371 compose->updating = FALSE;
2372 compose_destroy(compose);
2377 compose->redirect_filename = filename;
2379 /* Set save folder */
2380 item = msginfo->folder;
2381 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2382 gchar *folderidentifier;
2384 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2385 folderidentifier = folder_item_get_identifier(item);
2386 compose_set_save_to(compose, folderidentifier);
2387 g_free(folderidentifier);
2390 compose_attach_parts(compose, msginfo);
2392 if (msginfo->subject)
2393 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2395 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2397 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2398 _("The body of the \"Redirect\" template has an error at line %d."));
2399 quote_fmt_reset_vartable();
2400 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2402 compose_colorize_signature(compose);
2405 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2406 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2407 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2409 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2410 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2413 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2418 if (compose->toolbar->draft_btn)
2419 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2420 if (compose->toolbar->insert_btn)
2421 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2422 if (compose->toolbar->attach_btn)
2423 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2424 if (compose->toolbar->sig_btn)
2425 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2426 if (compose->toolbar->exteditor_btn)
2427 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2428 if (compose->toolbar->linewrap_current_btn)
2429 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2430 if (compose->toolbar->linewrap_all_btn)
2431 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2433 compose->modified = FALSE;
2434 compose_set_title(compose);
2435 compose->updating = FALSE;
2436 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2437 SCROLL_TO_CURSOR(compose);
2439 if (compose->deferred_destroy) {
2440 compose_destroy(compose);
2444 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2449 GList *compose_get_compose_list(void)
2451 return compose_list;
2454 void compose_entry_append(Compose *compose, const gchar *address,
2455 ComposeEntryType type, ComposePrefType pref_type)
2457 const gchar *header;
2459 gboolean in_quote = FALSE;
2460 if (!address || *address == '\0') return;
2467 header = N_("Bcc:");
2469 case COMPOSE_REPLYTO:
2470 header = N_("Reply-To:");
2472 case COMPOSE_NEWSGROUPS:
2473 header = N_("Newsgroups:");
2475 case COMPOSE_FOLLOWUPTO:
2476 header = N_( "Followup-To:");
2478 case COMPOSE_INREPLYTO:
2479 header = N_( "In-Reply-To:");
2486 header = prefs_common_translated_header_name(header);
2488 cur = begin = (gchar *)address;
2490 /* we separate the line by commas, but not if we're inside a quoted
2492 while (*cur != '\0') {
2494 in_quote = !in_quote;
2495 if (*cur == ',' && !in_quote) {
2496 gchar *tmp = g_strdup(begin);
2498 tmp[cur-begin]='\0';
2501 while (*tmp == ' ' || *tmp == '\t')
2503 compose_add_header_entry(compose, header, tmp, pref_type);
2510 gchar *tmp = g_strdup(begin);
2512 tmp[cur-begin]='\0';
2515 while (*tmp == ' ' || *tmp == '\t')
2517 compose_add_header_entry(compose, header, tmp, pref_type);
2522 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2524 static GdkColor yellow;
2525 static GdkColor black;
2526 static gboolean yellow_initialised = FALSE;
2530 if (!yellow_initialised) {
2531 gdk_color_parse("#f5f6be", &yellow);
2532 gdk_color_parse("#000000", &black);
2533 yellow_initialised = gdk_colormap_alloc_color(
2534 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2535 yellow_initialised &= gdk_colormap_alloc_color(
2536 gdk_colormap_get_system(), &black, FALSE, TRUE);
2539 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2540 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2541 if (gtk_entry_get_text(entry) &&
2542 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2543 if (yellow_initialised) {
2544 gtk_widget_modify_base(
2545 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2546 GTK_STATE_NORMAL, &yellow);
2547 gtk_widget_modify_text(
2548 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2549 GTK_STATE_NORMAL, &black);
2555 void compose_toolbar_cb(gint action, gpointer data)
2557 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2558 Compose *compose = (Compose*)toolbar_item->parent;
2560 cm_return_if_fail(compose != NULL);
2564 compose_send_cb(NULL, compose);
2567 compose_send_later_cb(NULL, compose);
2570 compose_draft(compose, COMPOSE_QUIT_EDITING);
2573 compose_insert_file_cb(NULL, compose);
2576 compose_attach_cb(NULL, compose);
2579 compose_insert_sig(compose, FALSE);
2582 compose_ext_editor_cb(NULL, compose);
2584 case A_LINEWRAP_CURRENT:
2585 compose_beautify_paragraph(compose, NULL, TRUE);
2587 case A_LINEWRAP_ALL:
2588 compose_wrap_all_full(compose, TRUE);
2591 compose_address_cb(NULL, compose);
2594 case A_CHECK_SPELLING:
2595 compose_check_all(NULL, compose);
2603 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2608 gchar *subject = NULL;
2612 gchar **attach = NULL;
2613 gchar *inreplyto = NULL;
2614 MailField mfield = NO_FIELD_PRESENT;
2616 /* get mailto parts but skip from */
2617 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2620 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2621 mfield = TO_FIELD_PRESENT;
2624 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2626 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2628 if (!g_utf8_validate (subject, -1, NULL)) {
2629 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2630 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2633 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2635 mfield = SUBJECT_FIELD_PRESENT;
2638 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2639 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2642 gboolean prev_autowrap = compose->autowrap;
2644 compose->autowrap = FALSE;
2646 mark = gtk_text_buffer_get_insert(buffer);
2647 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2649 if (!g_utf8_validate (body, -1, NULL)) {
2650 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2651 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2654 gtk_text_buffer_insert(buffer, &iter, body, -1);
2656 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2658 compose->autowrap = prev_autowrap;
2659 if (compose->autowrap)
2660 compose_wrap_all(compose);
2661 mfield = BODY_FIELD_PRESENT;
2665 gint i = 0, att = 0;
2666 gchar *warn_files = NULL;
2667 while (attach[i] != NULL) {
2668 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2669 if (utf8_filename) {
2670 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2671 gchar *tmp = g_strdup_printf("%s%s\n",
2672 warn_files?warn_files:"",
2678 g_free(utf8_filename);
2680 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2685 alertpanel_notice(ngettext(
2686 "The following file has been attached: \n%s",
2687 "The following files have been attached: \n%s", att), warn_files);
2692 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2705 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2707 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2708 {"Cc:", NULL, TRUE},
2709 {"References:", NULL, FALSE},
2710 {"Bcc:", NULL, TRUE},
2711 {"Newsgroups:", NULL, TRUE},
2712 {"Followup-To:", NULL, TRUE},
2713 {"List-Post:", NULL, FALSE},
2714 {"X-Priority:", NULL, FALSE},
2715 {NULL, NULL, FALSE}};
2731 cm_return_val_if_fail(msginfo != NULL, -1);
2733 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2734 procheader_get_header_fields(fp, hentry);
2737 if (hentry[H_REPLY_TO].body != NULL) {
2738 if (hentry[H_REPLY_TO].body[0] != '\0') {
2740 conv_unmime_header(hentry[H_REPLY_TO].body,
2743 g_free(hentry[H_REPLY_TO].body);
2744 hentry[H_REPLY_TO].body = NULL;
2746 if (hentry[H_CC].body != NULL) {
2747 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2748 g_free(hentry[H_CC].body);
2749 hentry[H_CC].body = NULL;
2751 if (hentry[H_REFERENCES].body != NULL) {
2752 if (compose->mode == COMPOSE_REEDIT)
2753 compose->references = hentry[H_REFERENCES].body;
2755 compose->references = compose_parse_references
2756 (hentry[H_REFERENCES].body, msginfo->msgid);
2757 g_free(hentry[H_REFERENCES].body);
2759 hentry[H_REFERENCES].body = NULL;
2761 if (hentry[H_BCC].body != NULL) {
2762 if (compose->mode == COMPOSE_REEDIT)
2764 conv_unmime_header(hentry[H_BCC].body, NULL);
2765 g_free(hentry[H_BCC].body);
2766 hentry[H_BCC].body = NULL;
2768 if (hentry[H_NEWSGROUPS].body != NULL) {
2769 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2770 hentry[H_NEWSGROUPS].body = NULL;
2772 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2773 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2774 compose->followup_to =
2775 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2778 g_free(hentry[H_FOLLOWUP_TO].body);
2779 hentry[H_FOLLOWUP_TO].body = NULL;
2781 if (hentry[H_LIST_POST].body != NULL) {
2782 gchar *to = NULL, *start = NULL;
2784 extract_address(hentry[H_LIST_POST].body);
2785 if (hentry[H_LIST_POST].body[0] != '\0') {
2786 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2788 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2789 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2792 g_free(compose->ml_post);
2793 compose->ml_post = to;
2796 g_free(hentry[H_LIST_POST].body);
2797 hentry[H_LIST_POST].body = NULL;
2800 /* CLAWS - X-Priority */
2801 if (compose->mode == COMPOSE_REEDIT)
2802 if (hentry[H_X_PRIORITY].body != NULL) {
2805 priority = atoi(hentry[H_X_PRIORITY].body);
2806 g_free(hentry[H_X_PRIORITY].body);
2808 hentry[H_X_PRIORITY].body = NULL;
2810 if (priority < PRIORITY_HIGHEST ||
2811 priority > PRIORITY_LOWEST)
2812 priority = PRIORITY_NORMAL;
2814 compose->priority = priority;
2817 if (compose->mode == COMPOSE_REEDIT) {
2818 if (msginfo->inreplyto && *msginfo->inreplyto)
2819 compose->inreplyto = g_strdup(msginfo->inreplyto);
2823 if (msginfo->msgid && *msginfo->msgid)
2824 compose->inreplyto = g_strdup(msginfo->msgid);
2826 if (!compose->references) {
2827 if (msginfo->msgid && *msginfo->msgid) {
2828 if (msginfo->inreplyto && *msginfo->inreplyto)
2829 compose->references =
2830 g_strdup_printf("<%s>\n\t<%s>",
2834 compose->references =
2835 g_strconcat("<", msginfo->msgid, ">",
2837 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2838 compose->references =
2839 g_strconcat("<", msginfo->inreplyto, ">",
2847 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2849 GSList *ref_id_list, *cur;
2853 ref_id_list = references_list_append(NULL, ref);
2854 if (!ref_id_list) return NULL;
2855 if (msgid && *msgid)
2856 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2861 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2862 /* "<" + Message-ID + ">" + CR+LF+TAB */
2863 len += strlen((gchar *)cur->data) + 5;
2865 if (len > MAX_REFERENCES_LEN) {
2866 /* remove second message-ID */
2867 if (ref_id_list && ref_id_list->next &&
2868 ref_id_list->next->next) {
2869 g_free(ref_id_list->next->data);
2870 ref_id_list = g_slist_remove
2871 (ref_id_list, ref_id_list->next->data);
2873 slist_free_strings(ref_id_list);
2874 g_slist_free(ref_id_list);
2881 new_ref = g_string_new("");
2882 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2883 if (new_ref->len > 0)
2884 g_string_append(new_ref, "\n\t");
2885 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2888 slist_free_strings(ref_id_list);
2889 g_slist_free(ref_id_list);
2891 new_ref_str = new_ref->str;
2892 g_string_free(new_ref, FALSE);
2897 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2898 const gchar *fmt, const gchar *qmark,
2899 const gchar *body, gboolean rewrap,
2900 gboolean need_unescape,
2901 const gchar *err_msg)
2903 MsgInfo* dummyinfo = NULL;
2904 gchar *quote_str = NULL;
2906 gboolean prev_autowrap;
2907 const gchar *trimmed_body = body;
2908 gint cursor_pos = -1;
2909 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2910 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2915 SIGNAL_BLOCK(buffer);
2918 dummyinfo = compose_msginfo_new_from_compose(compose);
2919 msginfo = dummyinfo;
2922 if (qmark != NULL) {
2924 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2925 compose->gtkaspell);
2927 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2929 quote_fmt_scan_string(qmark);
2932 buf = quote_fmt_get_buffer();
2934 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2936 Xstrdup_a(quote_str, buf, goto error)
2939 if (fmt && *fmt != '\0') {
2942 while (*trimmed_body == '\n')
2946 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2947 compose->gtkaspell);
2949 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2951 if (need_unescape) {
2954 /* decode \-escape sequences in the internal representation of the quote format */
2955 tmp = malloc(strlen(fmt)+1);
2956 pref_get_unescaped_pref(tmp, fmt);
2957 quote_fmt_scan_string(tmp);
2961 quote_fmt_scan_string(fmt);
2965 buf = quote_fmt_get_buffer();
2967 gint line = quote_fmt_get_line();
2968 alertpanel_error(err_msg, line);
2974 prev_autowrap = compose->autowrap;
2975 compose->autowrap = FALSE;
2977 mark = gtk_text_buffer_get_insert(buffer);
2978 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2979 if (g_utf8_validate(buf, -1, NULL)) {
2980 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2982 gchar *tmpout = NULL;
2983 tmpout = conv_codeset_strdup
2984 (buf, conv_get_locale_charset_str_no_utf8(),
2986 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2988 tmpout = g_malloc(strlen(buf)*2+1);
2989 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2991 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2995 cursor_pos = quote_fmt_get_cursor_pos();
2996 if (cursor_pos == -1)
2997 cursor_pos = gtk_text_iter_get_offset(&iter);
2998 compose->set_cursor_pos = cursor_pos;
3000 gtk_text_buffer_get_start_iter(buffer, &iter);
3001 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3002 gtk_text_buffer_place_cursor(buffer, &iter);
3004 compose->autowrap = prev_autowrap;
3005 if (compose->autowrap && rewrap)
3006 compose_wrap_all(compose);
3013 SIGNAL_UNBLOCK(buffer);
3015 procmsg_msginfo_free( dummyinfo );
3020 /* if ml_post is of type addr@host and from is of type
3021 * addr-anything@host, return TRUE
3023 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3025 gchar *left_ml = NULL;
3026 gchar *right_ml = NULL;
3027 gchar *left_from = NULL;
3028 gchar *right_from = NULL;
3029 gboolean result = FALSE;
3031 if (!ml_post || !from)
3034 left_ml = g_strdup(ml_post);
3035 if (strstr(left_ml, "@")) {
3036 right_ml = strstr(left_ml, "@")+1;
3037 *(strstr(left_ml, "@")) = '\0';
3040 left_from = g_strdup(from);
3041 if (strstr(left_from, "@")) {
3042 right_from = strstr(left_from, "@")+1;
3043 *(strstr(left_from, "@")) = '\0';
3046 if (left_ml && left_from && right_ml && right_from
3047 && !strncmp(left_from, left_ml, strlen(left_ml))
3048 && !strcmp(right_from, right_ml)) {
3057 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3058 gboolean respect_default_to)
3062 if (!folder || !folder->prefs)
3065 if (respect_default_to && folder->prefs->enable_default_to) {
3066 compose_entry_append(compose, folder->prefs->default_to,
3067 COMPOSE_TO, PREF_FOLDER);
3068 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3070 if (folder->prefs->enable_default_cc)
3071 compose_entry_append(compose, folder->prefs->default_cc,
3072 COMPOSE_CC, PREF_FOLDER);
3073 if (folder->prefs->enable_default_bcc)
3074 compose_entry_append(compose, folder->prefs->default_bcc,
3075 COMPOSE_BCC, PREF_FOLDER);
3076 if (folder->prefs->enable_default_replyto)
3077 compose_entry_append(compose, folder->prefs->default_replyto,
3078 COMPOSE_REPLYTO, PREF_FOLDER);
3081 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3086 if (!compose || !msginfo)
3089 if (msginfo->subject && *msginfo->subject) {
3090 buf = p = g_strdup(msginfo->subject);
3091 p += subject_get_prefix_length(p);
3092 memmove(buf, p, strlen(p) + 1);
3094 buf2 = g_strdup_printf("Re: %s", buf);
3095 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3100 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3103 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3104 gboolean to_all, gboolean to_ml,
3106 gboolean followup_and_reply_to)
3108 GSList *cc_list = NULL;
3111 gchar *replyto = NULL;
3112 gchar *ac_email = NULL;
3114 gboolean reply_to_ml = FALSE;
3115 gboolean default_reply_to = FALSE;
3117 cm_return_if_fail(compose->account != NULL);
3118 cm_return_if_fail(msginfo != NULL);
3120 reply_to_ml = to_ml && compose->ml_post;
3122 default_reply_to = msginfo->folder &&
3123 msginfo->folder->prefs->enable_default_reply_to;
3125 if (compose->account->protocol != A_NNTP) {
3126 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3128 if (reply_to_ml && !default_reply_to) {
3130 gboolean is_subscr = is_subscription(compose->ml_post,
3133 /* normal answer to ml post with a reply-to */
3134 compose_entry_append(compose,
3136 COMPOSE_TO, PREF_ML);
3137 if (compose->replyto)
3138 compose_entry_append(compose,
3140 COMPOSE_CC, PREF_ML);
3142 /* answer to subscription confirmation */
3143 if (compose->replyto)
3144 compose_entry_append(compose,
3146 COMPOSE_TO, PREF_ML);
3147 else if (msginfo->from)
3148 compose_entry_append(compose,
3150 COMPOSE_TO, PREF_ML);
3153 else if (!(to_all || to_sender) && default_reply_to) {
3154 compose_entry_append(compose,
3155 msginfo->folder->prefs->default_reply_to,
3156 COMPOSE_TO, PREF_FOLDER);
3157 compose_entry_mark_default_to(compose,
3158 msginfo->folder->prefs->default_reply_to);
3163 Xstrdup_a(tmp1, msginfo->from, return);
3164 extract_address(tmp1);
3165 if (to_all || to_sender ||
3166 !account_find_from_address(tmp1, FALSE))
3167 compose_entry_append(compose,
3168 (compose->replyto && !to_sender)
3169 ? compose->replyto :
3170 msginfo->from ? msginfo->from : "",
3171 COMPOSE_TO, PREF_NONE);
3172 else if (!to_all && !to_sender) {
3173 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3174 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3175 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3176 if (compose->replyto) {
3177 compose_entry_append(compose,
3179 COMPOSE_TO, PREF_NONE);
3181 compose_entry_append(compose,
3182 msginfo->from ? msginfo->from : "",
3183 COMPOSE_TO, PREF_NONE);
3186 /* replying to own mail, use original recp */
3187 compose_entry_append(compose,
3188 msginfo->to ? msginfo->to : "",
3189 COMPOSE_TO, PREF_NONE);
3190 compose_entry_append(compose,
3191 msginfo->cc ? msginfo->cc : "",
3192 COMPOSE_CC, PREF_NONE);
3197 if ((to_sender || to_ml) && prefs_common.reply_account_autosel)
3198 compose_check_for_email_account(compose);
3200 if (to_sender || (compose->followup_to &&
3201 !strncmp(compose->followup_to, "poster", 6)))
3202 compose_entry_append
3204 (compose->replyto ? compose->replyto :
3205 msginfo->from ? msginfo->from : ""),
3206 COMPOSE_TO, PREF_NONE);
3208 else if (followup_and_reply_to || to_all) {
3209 compose_entry_append
3211 (compose->replyto ? compose->replyto :
3212 msginfo->from ? msginfo->from : ""),
3213 COMPOSE_TO, PREF_NONE);
3215 compose_entry_append
3217 compose->followup_to ? compose->followup_to :
3218 compose->newsgroups ? compose->newsgroups : "",
3219 COMPOSE_NEWSGROUPS, PREF_NONE);
3220 } else if (reply_to_ml) {
3221 compose_entry_append(compose,
3222 compose->ml_post, COMPOSE_TO, PREF_ML);
3224 compose_entry_append
3226 compose->followup_to ? compose->followup_to :
3227 compose->newsgroups ? compose->newsgroups : "",
3228 COMPOSE_NEWSGROUPS, PREF_NONE);
3230 compose_reply_set_subject(compose, msginfo);
3232 if (to_ml && compose->ml_post) return;
3233 if (!to_all) return;
3235 if (compose->replyto) {
3236 Xstrdup_a(replyto, compose->replyto, return);
3237 extract_address(replyto);
3239 if (msginfo->from) {
3240 Xstrdup_a(from, msginfo->from, return);
3241 extract_address(from);
3244 if (replyto && from)
3245 cc_list = address_list_append_with_comments(cc_list, from);
3246 if (to_all && msginfo->folder &&
3247 msginfo->folder->prefs->enable_default_reply_to)
3248 cc_list = address_list_append_with_comments(cc_list,
3249 msginfo->folder->prefs->default_reply_to);
3250 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3251 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3253 ac_email = g_utf8_strdown(compose->account->address, -1);
3256 for (cur = cc_list; cur != NULL; cur = cur->next) {
3257 gchar *addr = g_utf8_strdown(cur->data, -1);
3258 extract_address(addr);
3260 if (strcmp(ac_email, addr))
3261 compose_entry_append(compose, (gchar *)cur->data,
3262 COMPOSE_CC, PREF_NONE);
3264 debug_print("Cc address same as compose account's, ignoring\n");
3269 slist_free_strings(cc_list);
3270 g_slist_free(cc_list);
3276 #define SET_ENTRY(entry, str) \
3279 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3282 #define SET_ADDRESS(type, str) \
3285 compose_entry_append(compose, str, type, PREF_NONE); \
3288 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3290 cm_return_if_fail(msginfo != NULL);
3292 SET_ENTRY(subject_entry, msginfo->subject);
3293 SET_ENTRY(from_name, msginfo->from);
3294 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3295 SET_ADDRESS(COMPOSE_CC, compose->cc);
3296 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3297 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3298 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3299 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3301 compose_update_priority_menu_item(compose);
3302 compose_update_privacy_system_menu_item(compose, FALSE);
3303 compose_show_first_last_header(compose, TRUE);
3309 static void compose_insert_sig(Compose *compose, gboolean replace)
3311 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3312 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3314 GtkTextIter iter, iter_end;
3315 gint cur_pos, ins_pos;
3316 gboolean prev_autowrap;
3317 gboolean found = FALSE;
3318 gboolean exists = FALSE;
3320 cm_return_if_fail(compose->account != NULL);
3324 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3325 G_CALLBACK(compose_changed_cb),
3328 mark = gtk_text_buffer_get_insert(buffer);
3329 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3330 cur_pos = gtk_text_iter_get_offset (&iter);
3333 gtk_text_buffer_get_end_iter(buffer, &iter);
3335 exists = (compose->sig_str != NULL);
3338 GtkTextIter first_iter, start_iter, end_iter;
3340 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3342 if (!exists || compose->sig_str[0] == '\0')
3345 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3346 compose->signature_tag);
3349 /* include previous \n\n */
3350 gtk_text_iter_backward_chars(&first_iter, 1);
3351 start_iter = first_iter;
3352 end_iter = first_iter;
3354 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3355 compose->signature_tag);
3356 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3357 compose->signature_tag);
3359 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3365 g_free(compose->sig_str);
3366 compose->sig_str = account_get_signature_str(compose->account);
3368 cur_pos = gtk_text_iter_get_offset(&iter);
3370 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3371 g_free(compose->sig_str);
3372 compose->sig_str = NULL;
3374 if (compose->sig_inserted == FALSE)
3375 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3376 compose->sig_inserted = TRUE;
3378 cur_pos = gtk_text_iter_get_offset(&iter);
3379 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3381 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3382 gtk_text_iter_forward_chars(&iter, 1);
3383 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3384 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3386 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3387 cur_pos = gtk_text_buffer_get_char_count (buffer);
3390 /* put the cursor where it should be
3391 * either where the quote_fmt says, either where it was */
3392 if (compose->set_cursor_pos < 0)
3393 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3395 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3396 compose->set_cursor_pos);
3398 compose->set_cursor_pos = -1;
3399 gtk_text_buffer_place_cursor(buffer, &iter);
3400 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3401 G_CALLBACK(compose_changed_cb),
3407 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3410 GtkTextBuffer *buffer;
3413 const gchar *cur_encoding;
3414 gchar buf[BUFFSIZE];
3417 gboolean prev_autowrap;
3418 gboolean badtxt = FALSE;
3419 struct stat file_stat;
3422 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3424 /* get the size of the file we are about to insert */
3425 ret = g_stat(file, &file_stat);
3427 gchar *shortfile = g_path_get_basename(file);
3428 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3430 return COMPOSE_INSERT_NO_FILE;
3431 } else if (prefs_common.warn_large_insert == TRUE) {
3433 /* ask user for confirmation if the file is large */
3434 if (prefs_common.warn_large_insert_size < 0 ||
3435 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3439 msg = g_strdup_printf(_("You are about to insert a file of %s "
3440 "in the message body. Are you sure you want to do that?"),
3441 to_human_readable(file_stat.st_size));
3442 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3443 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3446 /* do we ask for confirmation next time? */
3447 if (aval & G_ALERTDISABLE) {
3448 /* no confirmation next time, disable feature in preferences */
3449 aval &= ~G_ALERTDISABLE;
3450 prefs_common.warn_large_insert = FALSE;
3453 /* abort file insertion if user canceled action */
3454 if (aval != G_ALERTALTERNATE) {
3455 return COMPOSE_INSERT_NO_FILE;
3461 if ((fp = g_fopen(file, "rb")) == NULL) {
3462 FILE_OP_ERROR(file, "fopen");
3463 return COMPOSE_INSERT_READ_ERROR;
3466 prev_autowrap = compose->autowrap;
3467 compose->autowrap = FALSE;
3469 text = GTK_TEXT_VIEW(compose->text);
3470 buffer = gtk_text_view_get_buffer(text);
3471 mark = gtk_text_buffer_get_insert(buffer);
3472 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3474 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3475 G_CALLBACK(text_inserted),
3478 cur_encoding = conv_get_locale_charset_str_no_utf8();
3480 while (fgets(buf, sizeof(buf), fp) != NULL) {
3483 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3484 str = g_strdup(buf);
3486 str = conv_codeset_strdup
3487 (buf, cur_encoding, CS_INTERNAL);
3490 /* strip <CR> if DOS/Windows file,
3491 replace <CR> with <LF> if Macintosh file. */
3494 if (len > 0 && str[len - 1] != '\n') {
3496 if (str[len] == '\r') str[len] = '\n';
3499 gtk_text_buffer_insert(buffer, &iter, str, -1);
3503 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3504 G_CALLBACK(text_inserted),
3506 compose->autowrap = prev_autowrap;
3507 if (compose->autowrap)
3508 compose_wrap_all(compose);
3513 return COMPOSE_INSERT_INVALID_CHARACTER;
3515 return COMPOSE_INSERT_SUCCESS;
3518 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3519 const gchar *filename,
3520 const gchar *content_type)
3528 GtkListStore *store;
3530 gboolean has_binary = FALSE;
3532 if (!is_file_exist(file)) {
3533 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3534 gboolean result = FALSE;
3535 if (file_from_uri && is_file_exist(file_from_uri)) {
3536 result = compose_attach_append(
3537 compose, file_from_uri,
3541 g_free(file_from_uri);
3544 alertpanel_error("File %s doesn't exist\n", filename);
3547 if ((size = get_file_size(file)) < 0) {
3548 alertpanel_error("Can't get file size of %s\n", filename);
3552 alertpanel_error(_("File %s is empty."), filename);
3555 if ((fp = g_fopen(file, "rb")) == NULL) {
3556 alertpanel_error(_("Can't read %s."), filename);
3561 ainfo = g_new0(AttachInfo, 1);
3562 auto_ainfo = g_auto_pointer_new_with_free
3563 (ainfo, (GFreeFunc) compose_attach_info_free);
3564 ainfo->file = g_strdup(file);
3567 ainfo->content_type = g_strdup(content_type);
3568 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3570 MsgFlags flags = {0, 0};
3572 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3573 ainfo->encoding = ENC_7BIT;
3575 ainfo->encoding = ENC_8BIT;
3577 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3578 if (msginfo && msginfo->subject)
3579 name = g_strdup(msginfo->subject);
3581 name = g_path_get_basename(filename ? filename : file);
3583 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3585 procmsg_msginfo_free(msginfo);
3587 if (!g_ascii_strncasecmp(content_type, "text", 4))
3588 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3590 ainfo->encoding = ENC_BASE64;
3591 name = g_path_get_basename(filename ? filename : file);
3592 ainfo->name = g_strdup(name);
3596 ainfo->content_type = procmime_get_mime_type(file);
3597 if (!ainfo->content_type) {
3598 ainfo->content_type =
3599 g_strdup("application/octet-stream");
3600 ainfo->encoding = ENC_BASE64;
3601 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3603 procmime_get_encoding_for_text_file(file, &has_binary);
3605 ainfo->encoding = ENC_BASE64;
3606 name = g_path_get_basename(filename ? filename : file);
3607 ainfo->name = g_strdup(name);
3611 if (ainfo->name != NULL
3612 && !strcmp(ainfo->name, ".")) {
3613 g_free(ainfo->name);
3617 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3618 g_free(ainfo->content_type);
3619 ainfo->content_type = g_strdup("application/octet-stream");
3622 ainfo->size = (goffset)size;
3623 size_text = to_human_readable((goffset)size);
3625 store = GTK_LIST_STORE(gtk_tree_view_get_model
3626 (GTK_TREE_VIEW(compose->attach_clist)));
3628 gtk_list_store_append(store, &iter);
3629 gtk_list_store_set(store, &iter,
3630 COL_MIMETYPE, ainfo->content_type,
3631 COL_SIZE, size_text,
3632 COL_NAME, ainfo->name,
3634 COL_AUTODATA, auto_ainfo,
3637 g_auto_pointer_free(auto_ainfo);
3638 compose_attach_update_label(compose);
3642 static void compose_use_signing(Compose *compose, gboolean use_signing)
3644 compose->use_signing = use_signing;
3645 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3648 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3650 compose->use_encryption = use_encryption;
3651 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3654 #define NEXT_PART_NOT_CHILD(info) \
3656 node = info->node; \
3657 while (node->children) \
3658 node = g_node_last_child(node); \
3659 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3662 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3666 MimeInfo *firsttext = NULL;
3667 MimeInfo *encrypted = NULL;
3670 const gchar *partname = NULL;
3672 mimeinfo = procmime_scan_message(msginfo);
3673 if (!mimeinfo) return;
3675 if (mimeinfo->node->children == NULL) {
3676 procmime_mimeinfo_free_all(mimeinfo);
3680 /* find first content part */
3681 child = (MimeInfo *) mimeinfo->node->children->data;
3682 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3683 child = (MimeInfo *)child->node->children->data;
3686 if (child->type == MIMETYPE_TEXT) {
3688 debug_print("First text part found\n");
3689 } else if (compose->mode == COMPOSE_REEDIT &&
3690 child->type == MIMETYPE_APPLICATION &&
3691 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3692 encrypted = (MimeInfo *)child->node->parent->data;
3695 child = (MimeInfo *) mimeinfo->node->children->data;
3696 while (child != NULL) {
3699 if (child == encrypted) {
3700 /* skip this part of tree */
3701 NEXT_PART_NOT_CHILD(child);
3705 if (child->type == MIMETYPE_MULTIPART) {
3706 /* get the actual content */
3707 child = procmime_mimeinfo_next(child);
3711 if (child == firsttext) {
3712 child = procmime_mimeinfo_next(child);
3716 outfile = procmime_get_tmp_file_name(child);
3717 if ((err = procmime_get_part(outfile, child)) < 0)
3718 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3720 gchar *content_type;
3722 content_type = procmime_get_content_type_str(child->type, child->subtype);
3724 /* if we meet a pgp signature, we don't attach it, but
3725 * we force signing. */
3726 if ((strcmp(content_type, "application/pgp-signature") &&
3727 strcmp(content_type, "application/pkcs7-signature") &&
3728 strcmp(content_type, "application/x-pkcs7-signature"))
3729 || compose->mode == COMPOSE_REDIRECT) {
3730 partname = procmime_mimeinfo_get_parameter(child, "filename");
3731 if (partname == NULL)
3732 partname = procmime_mimeinfo_get_parameter(child, "name");
3733 if (partname == NULL)
3735 compose_attach_append(compose, outfile,
3736 partname, content_type);
3738 compose_force_signing(compose, compose->account, NULL);
3740 g_free(content_type);
3743 NEXT_PART_NOT_CHILD(child);
3745 procmime_mimeinfo_free_all(mimeinfo);
3748 #undef NEXT_PART_NOT_CHILD
3753 WAIT_FOR_INDENT_CHAR,
3754 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3757 /* return indent length, we allow:
3758 indent characters followed by indent characters or spaces/tabs,
3759 alphabets and numbers immediately followed by indent characters,
3760 and the repeating sequences of the above
3761 If quote ends with multiple spaces, only the first one is included. */
3762 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3763 const GtkTextIter *start, gint *len)
3765 GtkTextIter iter = *start;
3769 IndentState state = WAIT_FOR_INDENT_CHAR;
3772 gint alnum_count = 0;
3773 gint space_count = 0;
3776 if (prefs_common.quote_chars == NULL) {
3780 while (!gtk_text_iter_ends_line(&iter)) {
3781 wc = gtk_text_iter_get_char(&iter);
3782 if (g_unichar_iswide(wc))
3784 clen = g_unichar_to_utf8(wc, ch);
3788 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3789 is_space = g_unichar_isspace(wc);
3791 if (state == WAIT_FOR_INDENT_CHAR) {
3792 if (!is_indent && !g_unichar_isalnum(wc))
3795 quote_len += alnum_count + space_count + 1;
3796 alnum_count = space_count = 0;
3797 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3800 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3801 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3805 else if (is_indent) {
3806 quote_len += alnum_count + space_count + 1;
3807 alnum_count = space_count = 0;
3810 state = WAIT_FOR_INDENT_CHAR;
3814 gtk_text_iter_forward_char(&iter);
3817 if (quote_len > 0 && space_count > 0)
3823 if (quote_len > 0) {
3825 gtk_text_iter_forward_chars(&iter, quote_len);
3826 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3832 /* return >0 if the line is itemized */
3833 static int compose_itemized_length(GtkTextBuffer *buffer,
3834 const GtkTextIter *start)
3836 GtkTextIter iter = *start;
3841 if (gtk_text_iter_ends_line(&iter))
3846 wc = gtk_text_iter_get_char(&iter);
3847 if (!g_unichar_isspace(wc))
3849 gtk_text_iter_forward_char(&iter);
3850 if (gtk_text_iter_ends_line(&iter))
3854 clen = g_unichar_to_utf8(wc, ch);
3858 if (!strchr("*-+", ch[0]))
3861 gtk_text_iter_forward_char(&iter);
3862 if (gtk_text_iter_ends_line(&iter))
3864 wc = gtk_text_iter_get_char(&iter);
3865 if (g_unichar_isspace(wc)) {
3871 /* return the string at the start of the itemization */
3872 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3873 const GtkTextIter *start)
3875 GtkTextIter iter = *start;
3878 GString *item_chars = g_string_new("");
3881 if (gtk_text_iter_ends_line(&iter))
3886 wc = gtk_text_iter_get_char(&iter);
3887 if (!g_unichar_isspace(wc))
3889 gtk_text_iter_forward_char(&iter);
3890 if (gtk_text_iter_ends_line(&iter))
3892 g_string_append_unichar(item_chars, wc);
3895 str = item_chars->str;
3896 g_string_free(item_chars, FALSE);
3900 /* return the number of spaces at a line's start */
3901 static int compose_left_offset_length(GtkTextBuffer *buffer,
3902 const GtkTextIter *start)
3904 GtkTextIter iter = *start;
3907 if (gtk_text_iter_ends_line(&iter))
3911 wc = gtk_text_iter_get_char(&iter);
3912 if (!g_unichar_isspace(wc))
3915 gtk_text_iter_forward_char(&iter);
3916 if (gtk_text_iter_ends_line(&iter))
3920 gtk_text_iter_forward_char(&iter);
3921 if (gtk_text_iter_ends_line(&iter))
3926 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3927 const GtkTextIter *start,
3928 GtkTextIter *break_pos,
3932 GtkTextIter iter = *start, line_end = *start;
3933 PangoLogAttr *attrs;
3940 gboolean can_break = FALSE;
3941 gboolean do_break = FALSE;
3942 gboolean was_white = FALSE;
3943 gboolean prev_dont_break = FALSE;
3945 gtk_text_iter_forward_to_line_end(&line_end);
3946 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3947 len = g_utf8_strlen(str, -1);
3951 g_warning("compose_get_line_break_pos: len = 0!\n");
3955 /* g_print("breaking line: %d: %s (len = %d)\n",
3956 gtk_text_iter_get_line(&iter), str, len); */
3958 attrs = g_new(PangoLogAttr, len + 1);
3960 pango_default_break(str, -1, NULL, attrs, len + 1);
3964 /* skip quote and leading spaces */
3965 for (i = 0; *p != '\0' && i < len; i++) {
3968 wc = g_utf8_get_char(p);
3969 if (i >= quote_len && !g_unichar_isspace(wc))
3971 if (g_unichar_iswide(wc))
3973 else if (*p == '\t')
3977 p = g_utf8_next_char(p);
3980 for (; *p != '\0' && i < len; i++) {
3981 PangoLogAttr *attr = attrs + i;
3985 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3988 was_white = attr->is_white;
3990 /* don't wrap URI */
3991 if ((uri_len = get_uri_len(p)) > 0) {
3993 if (pos > 0 && col > max_col) {
4003 wc = g_utf8_get_char(p);
4004 if (g_unichar_iswide(wc)) {
4006 if (prev_dont_break && can_break && attr->is_line_break)
4008 } else if (*p == '\t')
4012 if (pos > 0 && col > max_col) {
4017 if (*p == '-' || *p == '/')
4018 prev_dont_break = TRUE;
4020 prev_dont_break = FALSE;
4022 p = g_utf8_next_char(p);
4026 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4031 *break_pos = *start;
4032 gtk_text_iter_set_line_offset(break_pos, pos);
4037 static gboolean compose_join_next_line(Compose *compose,
4038 GtkTextBuffer *buffer,
4040 const gchar *quote_str)
4042 GtkTextIter iter_ = *iter, cur, prev, next, end;
4043 PangoLogAttr attrs[3];
4045 gchar *next_quote_str;
4048 gboolean keep_cursor = FALSE;
4050 if (!gtk_text_iter_forward_line(&iter_) ||
4051 gtk_text_iter_ends_line(&iter_)) {
4054 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4056 if ((quote_str || next_quote_str) &&
4057 strcmp2(quote_str, next_quote_str) != 0) {
4058 g_free(next_quote_str);
4061 g_free(next_quote_str);
4064 if (quote_len > 0) {
4065 gtk_text_iter_forward_chars(&end, quote_len);
4066 if (gtk_text_iter_ends_line(&end)) {
4071 /* don't join itemized lines */
4072 if (compose_itemized_length(buffer, &end) > 0) {
4076 /* don't join signature separator */
4077 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4080 /* delete quote str */
4082 gtk_text_buffer_delete(buffer, &iter_, &end);
4084 /* don't join line breaks put by the user */
4086 gtk_text_iter_backward_char(&cur);
4087 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4088 gtk_text_iter_forward_char(&cur);
4092 gtk_text_iter_forward_char(&cur);
4093 /* delete linebreak and extra spaces */
4094 while (gtk_text_iter_backward_char(&cur)) {
4095 wc1 = gtk_text_iter_get_char(&cur);
4096 if (!g_unichar_isspace(wc1))
4101 while (!gtk_text_iter_ends_line(&cur)) {
4102 wc1 = gtk_text_iter_get_char(&cur);
4103 if (!g_unichar_isspace(wc1))
4105 gtk_text_iter_forward_char(&cur);
4108 if (!gtk_text_iter_equal(&prev, &next)) {
4111 mark = gtk_text_buffer_get_insert(buffer);
4112 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4113 if (gtk_text_iter_equal(&prev, &cur))
4115 gtk_text_buffer_delete(buffer, &prev, &next);
4119 /* insert space if required */
4120 gtk_text_iter_backward_char(&prev);
4121 wc1 = gtk_text_iter_get_char(&prev);
4122 wc2 = gtk_text_iter_get_char(&next);
4123 gtk_text_iter_forward_char(&next);
4124 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4125 pango_default_break(str, -1, NULL, attrs, 3);
4126 if (!attrs[1].is_line_break ||
4127 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4128 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4130 gtk_text_iter_backward_char(&iter_);
4131 gtk_text_buffer_place_cursor(buffer, &iter_);
4140 #define ADD_TXT_POS(bp_, ep_, pti_) \
4141 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4142 last = last->next; \
4143 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4144 last->next = NULL; \
4146 g_warning("alloc error scanning URIs\n"); \
4149 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4151 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4152 GtkTextBuffer *buffer;
4153 GtkTextIter iter, break_pos, end_of_line;
4154 gchar *quote_str = NULL;
4156 gboolean wrap_quote = prefs_common.linewrap_quote;
4157 gboolean prev_autowrap = compose->autowrap;
4158 gint startq_offset = -1, noq_offset = -1;
4159 gint uri_start = -1, uri_stop = -1;
4160 gint nouri_start = -1, nouri_stop = -1;
4161 gint num_blocks = 0;
4162 gint quotelevel = -1;
4163 gboolean modified = force;
4164 gboolean removed = FALSE;
4165 gboolean modified_before_remove = FALSE;
4167 gboolean start = TRUE;
4168 gint itemized_len = 0, rem_item_len = 0;
4169 gchar *itemized_chars = NULL;
4170 gboolean item_continuation = FALSE;
4175 if (compose->draft_timeout_tag == -2) {
4179 compose->autowrap = FALSE;
4181 buffer = gtk_text_view_get_buffer(text);
4182 undo_wrapping(compose->undostruct, TRUE);
4187 mark = gtk_text_buffer_get_insert(buffer);
4188 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4192 if (compose->draft_timeout_tag == -2) {
4193 if (gtk_text_iter_ends_line(&iter)) {
4194 while (gtk_text_iter_ends_line(&iter) &&
4195 gtk_text_iter_forward_line(&iter))
4198 while (gtk_text_iter_backward_line(&iter)) {
4199 if (gtk_text_iter_ends_line(&iter)) {
4200 gtk_text_iter_forward_line(&iter);
4206 /* move to line start */
4207 gtk_text_iter_set_line_offset(&iter, 0);
4210 itemized_len = compose_itemized_length(buffer, &iter);
4212 if (!itemized_len) {
4213 itemized_len = compose_left_offset_length(buffer, &iter);
4214 item_continuation = TRUE;
4218 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4220 /* go until paragraph end (empty line) */
4221 while (start || !gtk_text_iter_ends_line(&iter)) {
4222 gchar *scanpos = NULL;
4223 /* parse table - in order of priority */
4225 const gchar *needle; /* token */
4227 /* token search function */
4228 gchar *(*search) (const gchar *haystack,
4229 const gchar *needle);
4230 /* part parsing function */
4231 gboolean (*parse) (const gchar *start,
4232 const gchar *scanpos,
4236 /* part to URI function */
4237 gchar *(*build_uri) (const gchar *bp,
4241 static struct table parser[] = {
4242 {"http://", strcasestr, get_uri_part, make_uri_string},
4243 {"https://", strcasestr, get_uri_part, make_uri_string},
4244 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4245 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4246 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4247 {"www.", strcasestr, get_uri_part, make_http_string},
4248 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4249 {"@", strcasestr, get_email_part, make_email_string}
4251 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4252 gint last_index = PARSE_ELEMS;
4254 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4258 if (!prev_autowrap && num_blocks == 0) {
4260 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4261 G_CALLBACK(text_inserted),
4264 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4267 uri_start = uri_stop = -1;
4269 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4272 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4273 if (startq_offset == -1)
4274 startq_offset = gtk_text_iter_get_offset(&iter);
4275 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4276 if (quotelevel > 2) {
4277 /* recycle colors */
4278 if (prefs_common.recycle_quote_colors)
4287 if (startq_offset == -1)
4288 noq_offset = gtk_text_iter_get_offset(&iter);
4292 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4295 if (gtk_text_iter_ends_line(&iter)) {
4297 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4298 prefs_common.linewrap_len,
4300 GtkTextIter prev, next, cur;
4301 if (prev_autowrap != FALSE || force) {
4302 compose->automatic_break = TRUE;
4304 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4305 compose->automatic_break = FALSE;
4306 if (itemized_len && compose->autoindent) {
4307 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4308 if (!item_continuation)
4309 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4311 } else if (quote_str && wrap_quote) {
4312 compose->automatic_break = TRUE;
4314 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4315 compose->automatic_break = FALSE;
4316 if (itemized_len && compose->autoindent) {
4317 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4318 if (!item_continuation)
4319 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4323 /* remove trailing spaces */
4325 rem_item_len = itemized_len;
4326 while (compose->autoindent && rem_item_len-- > 0)
4327 gtk_text_iter_backward_char(&cur);
4328 gtk_text_iter_backward_char(&cur);
4331 while (!gtk_text_iter_starts_line(&cur)) {
4334 gtk_text_iter_backward_char(&cur);
4335 wc = gtk_text_iter_get_char(&cur);
4336 if (!g_unichar_isspace(wc))
4340 if (!gtk_text_iter_equal(&prev, &next)) {
4341 gtk_text_buffer_delete(buffer, &prev, &next);
4343 gtk_text_iter_forward_char(&break_pos);
4347 gtk_text_buffer_insert(buffer, &break_pos,
4351 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4353 /* move iter to current line start */
4354 gtk_text_iter_set_line_offset(&iter, 0);
4361 /* move iter to next line start */
4367 if (!prev_autowrap && num_blocks > 0) {
4369 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4370 G_CALLBACK(text_inserted),
4374 while (!gtk_text_iter_ends_line(&end_of_line)) {
4375 gtk_text_iter_forward_char(&end_of_line);
4377 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4379 nouri_start = gtk_text_iter_get_offset(&iter);
4380 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4382 walk_pos = gtk_text_iter_get_offset(&iter);
4383 /* FIXME: this looks phony. scanning for anything in the parse table */
4384 for (n = 0; n < PARSE_ELEMS; n++) {
4387 tmp = parser[n].search(walk, parser[n].needle);
4389 if (scanpos == NULL || tmp < scanpos) {
4398 /* check if URI can be parsed */
4399 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4400 (const gchar **)&ep, FALSE)
4401 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4405 strlen(parser[last_index].needle);
4408 uri_start = walk_pos + (bp - o_walk);
4409 uri_stop = walk_pos + (ep - o_walk);
4413 gtk_text_iter_forward_line(&iter);
4416 if (startq_offset != -1) {
4417 GtkTextIter startquote, endquote;
4418 gtk_text_buffer_get_iter_at_offset(
4419 buffer, &startquote, startq_offset);
4422 switch (quotelevel) {
4424 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4425 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4426 gtk_text_buffer_apply_tag_by_name(
4427 buffer, "quote0", &startquote, &endquote);
4428 gtk_text_buffer_remove_tag_by_name(
4429 buffer, "quote1", &startquote, &endquote);
4430 gtk_text_buffer_remove_tag_by_name(
4431 buffer, "quote2", &startquote, &endquote);
4436 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4437 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4438 gtk_text_buffer_apply_tag_by_name(
4439 buffer, "quote1", &startquote, &endquote);
4440 gtk_text_buffer_remove_tag_by_name(
4441 buffer, "quote0", &startquote, &endquote);
4442 gtk_text_buffer_remove_tag_by_name(
4443 buffer, "quote2", &startquote, &endquote);
4448 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4449 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4450 gtk_text_buffer_apply_tag_by_name(
4451 buffer, "quote2", &startquote, &endquote);
4452 gtk_text_buffer_remove_tag_by_name(
4453 buffer, "quote0", &startquote, &endquote);
4454 gtk_text_buffer_remove_tag_by_name(
4455 buffer, "quote1", &startquote, &endquote);
4461 } else if (noq_offset != -1) {
4462 GtkTextIter startnoquote, endnoquote;
4463 gtk_text_buffer_get_iter_at_offset(
4464 buffer, &startnoquote, noq_offset);
4467 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4468 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4469 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4470 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4471 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4472 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4473 gtk_text_buffer_remove_tag_by_name(
4474 buffer, "quote0", &startnoquote, &endnoquote);
4475 gtk_text_buffer_remove_tag_by_name(
4476 buffer, "quote1", &startnoquote, &endnoquote);
4477 gtk_text_buffer_remove_tag_by_name(
4478 buffer, "quote2", &startnoquote, &endnoquote);
4484 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4485 GtkTextIter nouri_start_iter, nouri_end_iter;
4486 gtk_text_buffer_get_iter_at_offset(
4487 buffer, &nouri_start_iter, nouri_start);
4488 gtk_text_buffer_get_iter_at_offset(
4489 buffer, &nouri_end_iter, nouri_stop);
4490 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4491 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4492 gtk_text_buffer_remove_tag_by_name(
4493 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4494 modified_before_remove = modified;
4499 if (uri_start >= 0 && uri_stop > 0) {
4500 GtkTextIter uri_start_iter, uri_end_iter, back;
4501 gtk_text_buffer_get_iter_at_offset(
4502 buffer, &uri_start_iter, uri_start);
4503 gtk_text_buffer_get_iter_at_offset(
4504 buffer, &uri_end_iter, uri_stop);
4505 back = uri_end_iter;
4506 gtk_text_iter_backward_char(&back);
4507 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4508 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4509 gtk_text_buffer_apply_tag_by_name(
4510 buffer, "link", &uri_start_iter, &uri_end_iter);
4512 if (removed && !modified_before_remove) {
4518 // debug_print("not modified, out after %d lines\n", lines);
4522 // debug_print("modified, out after %d lines\n", lines);
4524 g_free(itemized_chars);
4527 undo_wrapping(compose->undostruct, FALSE);
4528 compose->autowrap = prev_autowrap;
4533 void compose_action_cb(void *data)
4535 Compose *compose = (Compose *)data;
4536 compose_wrap_all(compose);
4539 static void compose_wrap_all(Compose *compose)
4541 compose_wrap_all_full(compose, FALSE);
4544 static void compose_wrap_all_full(Compose *compose, gboolean force)
4546 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4547 GtkTextBuffer *buffer;
4549 gboolean modified = TRUE;
4551 buffer = gtk_text_view_get_buffer(text);
4553 gtk_text_buffer_get_start_iter(buffer, &iter);
4554 while (!gtk_text_iter_is_end(&iter) && modified)
4555 modified = compose_beautify_paragraph(compose, &iter, force);
4559 static void compose_set_title(Compose *compose)
4565 edited = compose->modified ? _(" [Edited]") : "";
4567 subject = gtk_editable_get_chars(
4568 GTK_EDITABLE(compose->subject_entry), 0, -1);
4570 #ifndef GENERIC_UMPC
4571 if (subject && strlen(subject))
4572 str = g_strdup_printf(_("%s - Compose message%s"),
4575 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4577 str = g_strdup(_("Compose message"));
4580 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4586 * compose_current_mail_account:
4588 * Find a current mail account (the currently selected account, or the
4589 * default account, if a news account is currently selected). If a
4590 * mail account cannot be found, display an error message.
4592 * Return value: Mail account, or NULL if not found.
4594 static PrefsAccount *
4595 compose_current_mail_account(void)
4599 if (cur_account && cur_account->protocol != A_NNTP)
4602 ac = account_get_default();
4603 if (!ac || ac->protocol == A_NNTP) {
4604 alertpanel_error(_("Account for sending mail is not specified.\n"
4605 "Please select a mail account before sending."));
4612 #define QUOTE_IF_REQUIRED(out, str) \
4614 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4618 len = strlen(str) + 3; \
4619 if ((__tmp = alloca(len)) == NULL) { \
4620 g_warning("can't allocate memory\n"); \
4621 g_string_free(header, TRUE); \
4624 g_snprintf(__tmp, len, "\"%s\"", str); \
4629 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4630 g_warning("can't allocate memory\n"); \
4631 g_string_free(header, TRUE); \
4634 strcpy(__tmp, str); \
4640 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4642 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4646 len = strlen(str) + 3; \
4647 if ((__tmp = alloca(len)) == NULL) { \
4648 g_warning("can't allocate memory\n"); \
4651 g_snprintf(__tmp, len, "\"%s\"", str); \
4656 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4657 g_warning("can't allocate memory\n"); \
4660 strcpy(__tmp, str); \
4666 static void compose_select_account(Compose *compose, PrefsAccount *account,
4669 gchar *from = NULL, *header;
4670 ComposeHeaderEntry *header_entry;
4672 cm_return_if_fail(account != NULL);
4674 compose->account = account;
4675 if (account->name && *account->name) {
4677 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4678 from = g_strdup_printf("%s <%s>",
4679 buf, account->address);
4680 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4682 from = g_strdup_printf("<%s>",
4684 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4689 compose_set_title(compose);
4691 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4692 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4694 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4695 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4696 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4698 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4700 activate_privacy_system(compose, account, FALSE);
4702 if (!init && compose->mode != COMPOSE_REDIRECT) {
4703 undo_block(compose->undostruct);
4704 compose_insert_sig(compose, TRUE);
4705 undo_unblock(compose->undostruct);
4708 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4709 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4711 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4712 if (account->protocol == A_NNTP) {
4713 if (!strcmp(header, _("To:")))
4714 combobox_select_by_text(
4715 GTK_COMBO_BOX(header_entry->combo),
4718 if (!strcmp(header, _("Newsgroups:")))
4719 combobox_select_by_text(
4720 GTK_COMBO_BOX(header_entry->combo),
4728 /* use account's dict info if set */
4729 if (compose->gtkaspell) {
4730 if (account->enable_default_dictionary)
4731 gtkaspell_change_dict(compose->gtkaspell,
4732 account->default_dictionary, FALSE);
4733 if (account->enable_default_alt_dictionary)
4734 gtkaspell_change_alt_dict(compose->gtkaspell,
4735 account->default_alt_dictionary);
4736 if (account->enable_default_dictionary
4737 || account->enable_default_alt_dictionary)
4738 compose_spell_menu_changed(compose);
4743 gboolean compose_check_for_valid_recipient(Compose *compose) {
4744 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4745 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4746 gboolean recipient_found = FALSE;
4750 /* free to and newsgroup list */
4751 slist_free_strings(compose->to_list);
4752 g_slist_free(compose->to_list);
4753 compose->to_list = NULL;
4755 slist_free_strings(compose->newsgroup_list);
4756 g_slist_free(compose->newsgroup_list);
4757 compose->newsgroup_list = NULL;
4759 /* search header entries for to and newsgroup entries */
4760 for (list = compose->header_list; list; list = list->next) {
4763 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4764 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4767 if (entry[0] != '\0') {
4768 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4769 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4770 compose->to_list = address_list_append(compose->to_list, entry);
4771 recipient_found = TRUE;
4774 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4775 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4776 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4777 recipient_found = TRUE;
4784 return recipient_found;
4787 static gboolean compose_check_for_set_recipients(Compose *compose)
4789 if (compose->account->set_autocc && compose->account->auto_cc) {
4790 gboolean found_other = FALSE;
4792 /* search header entries for to and newsgroup entries */
4793 for (list = compose->header_list; list; list = list->next) {
4796 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4797 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4800 if (strcmp(entry, compose->account->auto_cc)
4801 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4811 if (compose->batch) {
4812 gtk_widget_show_all(compose->window);
4814 aval = alertpanel(_("Send"),
4815 _("The only recipient is the default CC address. Send anyway?"),
4816 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4817 if (aval != G_ALERTALTERNATE)
4821 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4822 gboolean found_other = FALSE;
4824 /* search header entries for to and newsgroup entries */
4825 for (list = compose->header_list; list; list = list->next) {
4828 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4829 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4832 if (strcmp(entry, compose->account->auto_bcc)
4833 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4843 if (compose->batch) {
4844 gtk_widget_show_all(compose->window);
4846 aval = alertpanel(_("Send"),
4847 _("The only recipient is the default BCC address. Send anyway?"),
4848 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4849 if (aval != G_ALERTALTERNATE)
4856 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4860 if (compose_check_for_valid_recipient(compose) == FALSE) {
4861 if (compose->batch) {
4862 gtk_widget_show_all(compose->window);
4864 alertpanel_error(_("Recipient is not specified."));
4868 if (compose_check_for_set_recipients(compose) == FALSE) {
4872 if (!compose->batch) {
4873 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4874 if (*str == '\0' && check_everything == TRUE &&
4875 compose->mode != COMPOSE_REDIRECT) {
4877 gchar *button_label;
4880 if (compose->sending)
4881 button_label = _("+_Send");
4883 button_label = _("+_Queue");
4884 message = g_strdup_printf(_("Subject is empty. %s"),
4885 compose->sending?_("Send it anyway?"):
4886 _("Queue it anyway?"));
4888 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4889 GTK_STOCK_CANCEL, button_label, NULL);
4891 if (aval != G_ALERTALTERNATE)
4896 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4902 gint compose_send(Compose *compose)
4905 FolderItem *folder = NULL;
4907 gchar *msgpath = NULL;
4908 gboolean discard_window = FALSE;
4909 gchar *errstr = NULL;
4910 gchar *tmsgid = NULL;
4911 MainWindow *mainwin = mainwindow_get_mainwindow();
4912 gboolean queued_removed = FALSE;
4914 if (prefs_common.send_dialog_invisible
4915 || compose->batch == TRUE)
4916 discard_window = TRUE;
4918 compose_allow_user_actions (compose, FALSE);
4919 compose->sending = TRUE;
4921 if (compose_check_entries(compose, TRUE) == FALSE) {
4922 if (compose->batch) {
4923 gtk_widget_show_all(compose->window);
4929 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4932 if (compose->batch) {
4933 gtk_widget_show_all(compose->window);
4936 alertpanel_error(_("Could not queue message for sending:\n\n"
4937 "Charset conversion failed."));
4938 } else if (val == -5) {
4939 alertpanel_error(_("Could not queue message for sending:\n\n"
4940 "Couldn't get recipient encryption key."));
4941 } else if (val == -6) {
4943 } else if (val == -3) {
4944 if (privacy_peek_error())
4945 alertpanel_error(_("Could not queue message for sending:\n\n"
4946 "Signature failed: %s"), privacy_get_error());
4947 } else if (val == -2 && errno != 0) {
4948 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4950 alertpanel_error(_("Could not queue message for sending."));
4955 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4956 if (discard_window) {
4957 compose->sending = FALSE;
4958 compose_close(compose);
4959 /* No more compose access in the normal codepath
4960 * after this point! */
4965 alertpanel_error(_("The message was queued but could not be "
4966 "sent.\nUse \"Send queued messages\" from "
4967 "the main window to retry."));
4968 if (!discard_window) {
4975 if (msgpath == NULL) {
4976 msgpath = folder_item_fetch_msg(folder, msgnum);
4977 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4980 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4981 claws_unlink(msgpath);
4984 if (!discard_window) {
4986 if (!queued_removed)
4987 folder_item_remove_msg(folder, msgnum);
4988 folder_item_scan(folder);
4990 /* make sure we delete that */
4991 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4993 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4994 folder_item_remove_msg(folder, tmp->msgnum);
4995 procmsg_msginfo_free(tmp);
5002 if (!queued_removed)
5003 folder_item_remove_msg(folder, msgnum);
5004 folder_item_scan(folder);
5006 /* make sure we delete that */
5007 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5009 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5010 folder_item_remove_msg(folder, tmp->msgnum);
5011 procmsg_msginfo_free(tmp);
5014 if (!discard_window) {
5015 compose->sending = FALSE;
5016 compose_allow_user_actions (compose, TRUE);
5017 compose_close(compose);
5021 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5022 "the main window to retry."), errstr);
5025 alertpanel_error_log(_("The message was queued but could not be "
5026 "sent.\nUse \"Send queued messages\" from "
5027 "the main window to retry."));
5029 if (!discard_window) {
5038 toolbar_main_set_sensitive(mainwin);
5039 main_window_set_menu_sensitive(mainwin);
5045 compose_allow_user_actions (compose, TRUE);
5046 compose->sending = FALSE;
5047 compose->modified = TRUE;
5048 toolbar_main_set_sensitive(mainwin);
5049 main_window_set_menu_sensitive(mainwin);
5054 static gboolean compose_use_attach(Compose *compose)
5056 GtkTreeModel *model = gtk_tree_view_get_model
5057 (GTK_TREE_VIEW(compose->attach_clist));
5058 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5061 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5064 gchar buf[BUFFSIZE];
5066 gboolean first_to_address;
5067 gboolean first_cc_address;
5069 ComposeHeaderEntry *headerentry;
5070 const gchar *headerentryname;
5071 const gchar *cc_hdr;
5072 const gchar *to_hdr;
5073 gboolean err = FALSE;
5075 debug_print("Writing redirect header\n");
5077 cc_hdr = prefs_common_translated_header_name("Cc:");
5078 to_hdr = prefs_common_translated_header_name("To:");
5080 first_to_address = TRUE;
5081 for (list = compose->header_list; list; list = list->next) {
5082 headerentry = ((ComposeHeaderEntry *)list->data);
5083 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5085 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5086 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5087 Xstrdup_a(str, entstr, return -1);
5089 if (str[0] != '\0') {
5090 compose_convert_header
5091 (compose, buf, sizeof(buf), str,
5092 strlen("Resent-To") + 2, TRUE);
5094 if (first_to_address) {
5095 err |= (fprintf(fp, "Resent-To: ") < 0);
5096 first_to_address = FALSE;
5098 err |= (fprintf(fp, ",") < 0);
5100 err |= (fprintf(fp, "%s", buf) < 0);
5104 if (!first_to_address) {
5105 err |= (fprintf(fp, "\n") < 0);
5108 first_cc_address = TRUE;
5109 for (list = compose->header_list; list; list = list->next) {
5110 headerentry = ((ComposeHeaderEntry *)list->data);
5111 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5113 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5114 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5115 Xstrdup_a(str, strg, return -1);
5117 if (str[0] != '\0') {
5118 compose_convert_header
5119 (compose, buf, sizeof(buf), str,
5120 strlen("Resent-Cc") + 2, TRUE);
5122 if (first_cc_address) {
5123 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5124 first_cc_address = FALSE;
5126 err |= (fprintf(fp, ",") < 0);
5128 err |= (fprintf(fp, "%s", buf) < 0);
5132 if (!first_cc_address) {
5133 err |= (fprintf(fp, "\n") < 0);
5136 return (err ? -1:0);
5139 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5141 gchar buf[BUFFSIZE];
5143 const gchar *entstr;
5144 /* struct utsname utsbuf; */
5145 gboolean err = FALSE;
5147 cm_return_val_if_fail(fp != NULL, -1);
5148 cm_return_val_if_fail(compose->account != NULL, -1);
5149 cm_return_val_if_fail(compose->account->address != NULL, -1);
5152 get_rfc822_date(buf, sizeof(buf));
5153 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5156 if (compose->account->name && *compose->account->name) {
5157 compose_convert_header
5158 (compose, buf, sizeof(buf), compose->account->name,
5159 strlen("From: "), TRUE);
5160 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5161 buf, compose->account->address) < 0);
5163 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5166 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5167 if (*entstr != '\0') {
5168 Xstrdup_a(str, entstr, return -1);
5171 compose_convert_header(compose, buf, sizeof(buf), str,
5172 strlen("Subject: "), FALSE);
5173 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5177 /* Resent-Message-ID */
5178 if (compose->account->set_domain && compose->account->domain) {
5179 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5180 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5181 g_snprintf(buf, sizeof(buf), "%s",
5182 strchr(compose->account->address, '@') ?
5183 strchr(compose->account->address, '@')+1 :
5184 compose->account->address);
5186 g_snprintf(buf, sizeof(buf), "%s", "");
5189 if (compose->account->gen_msgid) {
5191 if (compose->account->msgid_with_addr) {
5192 addr = compose->account->address;
5194 generate_msgid(buf, sizeof(buf), addr);
5195 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5196 compose->msgid = g_strdup(buf);
5198 compose->msgid = NULL;
5201 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5204 /* separator between header and body */
5205 err |= (fputs("\n", fp) == EOF);
5207 return (err ? -1:0);
5210 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5214 gchar buf[BUFFSIZE];
5216 gboolean skip = FALSE;
5217 gboolean err = FALSE;
5218 gchar *not_included[]={
5219 "Return-Path:", "Delivered-To:", "Received:",
5220 "Subject:", "X-UIDL:", "AF:",
5221 "NF:", "PS:", "SRH:",
5222 "SFN:", "DSR:", "MID:",
5223 "CFG:", "PT:", "S:",
5224 "RQ:", "SSV:", "NSV:",
5225 "SSH:", "R:", "MAID:",
5226 "NAID:", "RMID:", "FMID:",
5227 "SCF:", "RRCPT:", "NG:",
5228 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5229 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5230 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5231 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5232 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5235 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5236 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5240 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5242 for (i = 0; not_included[i] != NULL; i++) {
5243 if (g_ascii_strncasecmp(buf, not_included[i],
5244 strlen(not_included[i])) == 0) {
5251 if (fputs(buf, fdest) == -1)
5254 if (!prefs_common.redirect_keep_from) {
5255 if (g_ascii_strncasecmp(buf, "From:",
5256 strlen("From:")) == 0) {
5257 err |= (fputs(" (by way of ", fdest) == EOF);
5258 if (compose->account->name
5259 && *compose->account->name) {
5260 compose_convert_header
5261 (compose, buf, sizeof(buf),
5262 compose->account->name,
5265 err |= (fprintf(fdest, "%s <%s>",
5267 compose->account->address) < 0);
5269 err |= (fprintf(fdest, "%s",
5270 compose->account->address) < 0);
5271 err |= (fputs(")", fdest) == EOF);
5275 if (fputs("\n", fdest) == -1)
5282 if (compose_redirect_write_headers(compose, fdest))
5285 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5286 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5299 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5301 GtkTextBuffer *buffer;
5302 GtkTextIter start, end;
5305 const gchar *out_codeset;
5306 EncodingType encoding = ENC_UNKNOWN;
5307 MimeInfo *mimemsg, *mimetext;
5309 const gchar *src_codeset = CS_INTERNAL;
5310 gchar *from_addr = NULL;
5311 gchar *from_name = NULL;
5313 if (action == COMPOSE_WRITE_FOR_SEND)
5314 attach_parts = TRUE;
5316 /* create message MimeInfo */
5317 mimemsg = procmime_mimeinfo_new();
5318 mimemsg->type = MIMETYPE_MESSAGE;
5319 mimemsg->subtype = g_strdup("rfc822");
5320 mimemsg->content = MIMECONTENT_MEM;
5321 mimemsg->tmp = TRUE; /* must free content later */
5322 mimemsg->data.mem = compose_get_header(compose);
5324 /* Create text part MimeInfo */
5325 /* get all composed text */
5326 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5327 gtk_text_buffer_get_start_iter(buffer, &start);
5328 gtk_text_buffer_get_end_iter(buffer, &end);
5329 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5331 out_codeset = conv_get_charset_str(compose->out_encoding);
5333 if (!out_codeset && is_ascii_str(chars)) {
5334 out_codeset = CS_US_ASCII;
5335 } else if (prefs_common.outgoing_fallback_to_ascii &&
5336 is_ascii_str(chars)) {
5337 out_codeset = CS_US_ASCII;
5338 encoding = ENC_7BIT;
5342 gchar *test_conv_global_out = NULL;
5343 gchar *test_conv_reply = NULL;
5345 /* automatic mode. be automatic. */
5346 codeconv_set_strict(TRUE);
5348 out_codeset = conv_get_outgoing_charset_str();
5350 debug_print("trying to convert to %s\n", out_codeset);
5351 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5354 if (!test_conv_global_out && compose->orig_charset
5355 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5356 out_codeset = compose->orig_charset;
5357 debug_print("failure; trying to convert to %s\n", out_codeset);
5358 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5361 if (!test_conv_global_out && !test_conv_reply) {
5363 out_codeset = CS_INTERNAL;
5364 debug_print("failure; finally using %s\n", out_codeset);
5366 g_free(test_conv_global_out);
5367 g_free(test_conv_reply);
5368 codeconv_set_strict(FALSE);
5371 if (encoding == ENC_UNKNOWN) {
5372 if (prefs_common.encoding_method == CTE_BASE64)
5373 encoding = ENC_BASE64;
5374 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5375 encoding = ENC_QUOTED_PRINTABLE;
5376 else if (prefs_common.encoding_method == CTE_8BIT)
5377 encoding = ENC_8BIT;
5379 encoding = procmime_get_encoding_for_charset(out_codeset);
5382 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5383 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5385 if (action == COMPOSE_WRITE_FOR_SEND) {
5386 codeconv_set_strict(TRUE);
5387 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5388 codeconv_set_strict(FALSE);
5394 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5395 "to the specified %s charset.\n"
5396 "Send it as %s?"), out_codeset, src_codeset);
5397 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5398 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5401 if (aval != G_ALERTALTERNATE) {
5406 out_codeset = src_codeset;
5412 out_codeset = src_codeset;
5417 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5418 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5419 strstr(buf, "\nFrom ") != NULL) {
5420 encoding = ENC_QUOTED_PRINTABLE;
5424 mimetext = procmime_mimeinfo_new();
5425 mimetext->content = MIMECONTENT_MEM;
5426 mimetext->tmp = TRUE; /* must free content later */
5427 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5428 * and free the data, which we need later. */
5429 mimetext->data.mem = g_strdup(buf);
5430 mimetext->type = MIMETYPE_TEXT;
5431 mimetext->subtype = g_strdup("plain");
5432 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5433 g_strdup(out_codeset));
5435 /* protect trailing spaces when signing message */
5436 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5437 privacy_system_can_sign(compose->privacy_system)) {
5438 encoding = ENC_QUOTED_PRINTABLE;
5441 debug_print("main text: %zd bytes encoded as %s in %d\n",
5442 strlen(buf), out_codeset, encoding);
5444 /* check for line length limit */
5445 if (action == COMPOSE_WRITE_FOR_SEND &&
5446 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5447 check_line_length(buf, 1000, &line) < 0) {
5451 msg = g_strdup_printf
5452 (_("Line %d exceeds the line length limit (998 bytes).\n"
5453 "The contents of the message might be broken on the way to the delivery.\n"
5455 "Send it anyway?"), line + 1);
5456 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5458 if (aval != G_ALERTALTERNATE) {
5464 if (encoding != ENC_UNKNOWN)
5465 procmime_encode_content(mimetext, encoding);
5467 /* append attachment parts */
5468 if (compose_use_attach(compose) && attach_parts) {
5469 MimeInfo *mimempart;
5470 gchar *boundary = NULL;
5471 mimempart = procmime_mimeinfo_new();
5472 mimempart->content = MIMECONTENT_EMPTY;
5473 mimempart->type = MIMETYPE_MULTIPART;
5474 mimempart->subtype = g_strdup("mixed");
5478 boundary = generate_mime_boundary(NULL);
5479 } while (strstr(buf, boundary) != NULL);
5481 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5484 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5486 g_node_append(mimempart->node, mimetext->node);
5487 g_node_append(mimemsg->node, mimempart->node);
5489 if (compose_add_attachments(compose, mimempart) < 0)
5492 g_node_append(mimemsg->node, mimetext->node);
5496 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5497 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5498 /* extract name and address */
5499 if (strstr(spec, " <") && strstr(spec, ">")) {
5500 from_addr = g_strdup(strrchr(spec, '<')+1);
5501 *(strrchr(from_addr, '>')) = '\0';
5502 from_name = g_strdup(spec);
5503 *(strrchr(from_name, '<')) = '\0';
5510 /* sign message if sending */
5511 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5512 privacy_system_can_sign(compose->privacy_system))
5513 if (!privacy_sign(compose->privacy_system, mimemsg,
5514 compose->account, from_addr)) {
5521 procmime_write_mimeinfo(mimemsg, fp);
5523 procmime_mimeinfo_free_all(mimemsg);
5528 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5530 GtkTextBuffer *buffer;
5531 GtkTextIter start, end;
5536 if ((fp = g_fopen(file, "wb")) == NULL) {
5537 FILE_OP_ERROR(file, "fopen");
5541 /* chmod for security */
5542 if (change_file_mode_rw(fp, file) < 0) {
5543 FILE_OP_ERROR(file, "chmod");
5544 g_warning("can't change file mode\n");
5547 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5548 gtk_text_buffer_get_start_iter(buffer, &start);
5549 gtk_text_buffer_get_end_iter(buffer, &end);
5550 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5552 chars = conv_codeset_strdup
5553 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5556 if (!chars) return -1;
5559 len = strlen(chars);
5560 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5561 FILE_OP_ERROR(file, "fwrite");
5570 if (fclose(fp) == EOF) {
5571 FILE_OP_ERROR(file, "fclose");
5578 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5581 MsgInfo *msginfo = compose->targetinfo;
5583 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5584 if (!msginfo) return -1;
5586 if (!force && MSG_IS_LOCKED(msginfo->flags))
5589 item = msginfo->folder;
5590 cm_return_val_if_fail(item != NULL, -1);
5592 if (procmsg_msg_exist(msginfo) &&
5593 (folder_has_parent_of_type(item, F_QUEUE) ||
5594 folder_has_parent_of_type(item, F_DRAFT)
5595 || msginfo == compose->autosaved_draft)) {
5596 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5597 g_warning("can't remove the old message\n");
5600 debug_print("removed reedit target %d\n", msginfo->msgnum);
5607 static void compose_remove_draft(Compose *compose)
5610 MsgInfo *msginfo = compose->targetinfo;
5611 drafts = account_get_special_folder(compose->account, F_DRAFT);
5613 if (procmsg_msg_exist(msginfo)) {
5614 folder_item_remove_msg(drafts, msginfo->msgnum);
5619 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5620 gboolean remove_reedit_target)
5622 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5625 static gboolean compose_warn_encryption(Compose *compose)
5627 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5628 AlertValue val = G_ALERTALTERNATE;
5630 if (warning == NULL)
5633 val = alertpanel_full(_("Encryption warning"), warning,
5634 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5635 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5636 if (val & G_ALERTDISABLE) {
5637 val &= ~G_ALERTDISABLE;
5638 if (val == G_ALERTALTERNATE)
5639 privacy_inhibit_encrypt_warning(compose->privacy_system,
5643 if (val == G_ALERTALTERNATE) {
5650 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5651 gchar **msgpath, gboolean check_subject,
5652 gboolean remove_reedit_target)
5659 static gboolean lock = FALSE;
5660 PrefsAccount *mailac = NULL, *newsac = NULL;
5661 gboolean err = FALSE;
5663 debug_print("queueing message...\n");
5664 cm_return_val_if_fail(compose->account != NULL, -1);
5668 if (compose_check_entries(compose, check_subject) == FALSE) {
5670 if (compose->batch) {
5671 gtk_widget_show_all(compose->window);
5676 if (!compose->to_list && !compose->newsgroup_list) {
5677 g_warning("can't get recipient list.");
5682 if (compose->to_list) {
5683 if (compose->account->protocol != A_NNTP)
5684 mailac = compose->account;
5685 else if (cur_account && cur_account->protocol != A_NNTP)
5686 mailac = cur_account;
5687 else if (!(mailac = compose_current_mail_account())) {
5689 alertpanel_error(_("No account for sending mails available!"));
5694 if (compose->newsgroup_list) {
5695 if (compose->account->protocol == A_NNTP)
5696 newsac = compose->account;
5699 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5704 /* write queue header */
5705 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5706 G_DIR_SEPARATOR, compose, (guint) rand());
5707 debug_print("queuing to %s\n", tmp);
5708 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5709 FILE_OP_ERROR(tmp, "fopen");
5715 if (change_file_mode_rw(fp, tmp) < 0) {
5716 FILE_OP_ERROR(tmp, "chmod");
5717 g_warning("can't change file mode\n");
5720 /* queueing variables */
5721 err |= (fprintf(fp, "AF:\n") < 0);
5722 err |= (fprintf(fp, "NF:0\n") < 0);
5723 err |= (fprintf(fp, "PS:10\n") < 0);
5724 err |= (fprintf(fp, "SRH:1\n") < 0);
5725 err |= (fprintf(fp, "SFN:\n") < 0);
5726 err |= (fprintf(fp, "DSR:\n") < 0);
5728 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5730 err |= (fprintf(fp, "MID:\n") < 0);
5731 err |= (fprintf(fp, "CFG:\n") < 0);
5732 err |= (fprintf(fp, "PT:0\n") < 0);
5733 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5734 err |= (fprintf(fp, "RQ:\n") < 0);
5736 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5738 err |= (fprintf(fp, "SSV:\n") < 0);
5740 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5742 err |= (fprintf(fp, "NSV:\n") < 0);
5743 err |= (fprintf(fp, "SSH:\n") < 0);
5744 /* write recepient list */
5745 if (compose->to_list) {
5746 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5747 for (cur = compose->to_list->next; cur != NULL;
5749 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5750 err |= (fprintf(fp, "\n") < 0);
5752 /* write newsgroup list */
5753 if (compose->newsgroup_list) {
5754 err |= (fprintf(fp, "NG:") < 0);
5755 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5756 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5757 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5758 err |= (fprintf(fp, "\n") < 0);
5760 /* Sylpheed account IDs */
5762 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5764 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5767 if (compose->privacy_system != NULL) {
5768 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5769 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5770 if (compose->use_encryption) {
5772 if (!compose_warn_encryption(compose)) {
5779 if (mailac && mailac->encrypt_to_self) {
5780 GSList *tmp_list = g_slist_copy(compose->to_list);
5781 tmp_list = g_slist_append(tmp_list, compose->account->address);
5782 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5783 g_slist_free(tmp_list);
5785 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5787 if (encdata != NULL) {
5788 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5789 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5790 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5792 } /* else we finally dont want to encrypt */
5794 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5795 /* and if encdata was null, it means there's been a problem in
5807 /* Save copy folder */
5808 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5809 gchar *savefolderid;
5811 savefolderid = compose_get_save_to(compose);
5812 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5813 g_free(savefolderid);
5815 /* Save copy folder */
5816 if (compose->return_receipt) {
5817 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5819 /* Message-ID of message replying to */
5820 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5823 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5824 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5827 /* Message-ID of message forwarding to */
5828 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5831 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5832 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5836 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5837 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5839 /* end of headers */
5840 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5842 if (compose->redirect_filename != NULL) {
5843 if (compose_redirect_write_to_file(compose, fp) < 0) {
5852 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5857 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5861 g_warning("failed to write queue message\n");
5868 if (fclose(fp) == EOF) {
5869 FILE_OP_ERROR(tmp, "fclose");
5876 if (item && *item) {
5879 queue = account_get_special_folder(compose->account, F_QUEUE);
5882 g_warning("can't find queue folder\n");
5888 folder_item_scan(queue);
5889 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5890 g_warning("can't queue the message\n");
5897 if (msgpath == NULL) {
5903 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5904 compose_remove_reedit_target(compose, FALSE);
5907 if ((msgnum != NULL) && (item != NULL)) {
5915 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5918 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5920 struct stat statbuf;
5921 gchar *type, *subtype;
5922 GtkTreeModel *model;
5925 model = gtk_tree_view_get_model(tree_view);
5927 if (!gtk_tree_model_get_iter_first(model, &iter))
5930 gtk_tree_model_get(model, &iter,
5934 if (!is_file_exist(ainfo->file)) {
5935 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5936 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5937 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5939 if (val == G_ALERTDEFAULT) {
5944 mimepart = procmime_mimeinfo_new();
5945 mimepart->content = MIMECONTENT_FILE;
5946 mimepart->data.filename = g_strdup(ainfo->file);
5947 mimepart->tmp = FALSE; /* or we destroy our attachment */
5948 mimepart->offset = 0;
5950 g_stat(ainfo->file, &statbuf);
5951 mimepart->length = statbuf.st_size;
5953 type = g_strdup(ainfo->content_type);
5955 if (!strchr(type, '/')) {
5957 type = g_strdup("application/octet-stream");
5960 subtype = strchr(type, '/') + 1;
5961 *(subtype - 1) = '\0';
5962 mimepart->type = procmime_get_media_type(type);
5963 mimepart->subtype = g_strdup(subtype);
5966 if (mimepart->type == MIMETYPE_MESSAGE &&
5967 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5968 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5971 if (mimepart->type == MIMETYPE_APPLICATION &&
5972 !strcmp2(mimepart->subtype, "octet-stream"))
5973 g_hash_table_insert(mimepart->typeparameters,
5974 g_strdup("name"), g_strdup(ainfo->name));
5975 g_hash_table_insert(mimepart->dispositionparameters,
5976 g_strdup("filename"), g_strdup(ainfo->name));
5977 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5981 if (compose->use_signing) {
5982 if (ainfo->encoding == ENC_7BIT)
5983 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5984 else if (ainfo->encoding == ENC_8BIT)
5985 ainfo->encoding = ENC_BASE64;
5988 procmime_encode_content(mimepart, ainfo->encoding);
5990 g_node_append(parent->node, mimepart->node);
5991 } while (gtk_tree_model_iter_next(model, &iter));
5996 #define IS_IN_CUSTOM_HEADER(header) \
5997 (compose->account->add_customhdr && \
5998 custom_header_find(compose->account->customhdr_list, header) != NULL)
6000 static void compose_add_headerfield_from_headerlist(Compose *compose,
6002 const gchar *fieldname,
6003 const gchar *seperator)
6005 gchar *str, *fieldname_w_colon;
6006 gboolean add_field = FALSE;
6008 ComposeHeaderEntry *headerentry;
6009 const gchar *headerentryname;
6010 const gchar *trans_fieldname;
6013 if (IS_IN_CUSTOM_HEADER(fieldname))
6016 debug_print("Adding %s-fields\n", fieldname);
6018 fieldstr = g_string_sized_new(64);
6020 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6021 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6023 for (list = compose->header_list; list; list = list->next) {
6024 headerentry = ((ComposeHeaderEntry *)list->data);
6025 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6027 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6028 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6030 if (str[0] != '\0') {
6032 g_string_append(fieldstr, seperator);
6033 g_string_append(fieldstr, str);
6042 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6043 compose_convert_header
6044 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6045 strlen(fieldname) + 2, TRUE);
6046 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6050 g_free(fieldname_w_colon);
6051 g_string_free(fieldstr, TRUE);
6056 static gchar *compose_get_header(Compose *compose)
6058 gchar buf[BUFFSIZE];
6059 const gchar *entry_str;
6063 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6065 gchar *from_name = NULL, *from_address = NULL;
6068 cm_return_val_if_fail(compose->account != NULL, NULL);
6069 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6071 header = g_string_sized_new(64);
6074 get_rfc822_date(buf, sizeof(buf));
6075 g_string_append_printf(header, "Date: %s\n", buf);
6079 if (compose->account->name && *compose->account->name) {
6081 QUOTE_IF_REQUIRED(buf, compose->account->name);
6082 tmp = g_strdup_printf("%s <%s>",
6083 buf, compose->account->address);
6085 tmp = g_strdup_printf("%s",
6086 compose->account->address);
6088 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6089 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6091 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6092 from_address = g_strdup(compose->account->address);
6094 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6095 /* extract name and address */
6096 if (strstr(spec, " <") && strstr(spec, ">")) {
6097 from_address = g_strdup(strrchr(spec, '<')+1);
6098 *(strrchr(from_address, '>')) = '\0';
6099 from_name = g_strdup(spec);
6100 *(strrchr(from_name, '<')) = '\0';
6103 from_address = g_strdup(spec);
6110 if (from_name && *from_name) {
6111 compose_convert_header
6112 (compose, buf, sizeof(buf), from_name,
6113 strlen("From: "), TRUE);
6114 QUOTE_IF_REQUIRED(name, buf);
6116 g_string_append_printf(header, "From: %s <%s>\n",
6117 name, from_address);
6119 g_string_append_printf(header, "From: %s\n", from_address);
6122 g_free(from_address);
6125 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6128 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6131 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6135 * If this account is a NNTP account remove Bcc header from
6136 * message body since it otherwise will be publicly shown
6138 if (compose->account->protocol != A_NNTP)
6139 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6142 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6144 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6147 compose_convert_header(compose, buf, sizeof(buf), str,
6148 strlen("Subject: "), FALSE);
6149 g_string_append_printf(header, "Subject: %s\n", buf);
6155 if (compose->account->set_domain && compose->account->domain) {
6156 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6157 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6158 g_snprintf(buf, sizeof(buf), "%s",
6159 strchr(compose->account->address, '@') ?
6160 strchr(compose->account->address, '@')+1 :
6161 compose->account->address);
6163 g_snprintf(buf, sizeof(buf), "%s", "");
6166 if (compose->account->gen_msgid) {
6168 if (compose->account->msgid_with_addr) {
6169 addr = compose->account->address;
6171 generate_msgid(buf, sizeof(buf), addr);
6172 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6173 compose->msgid = g_strdup(buf);
6175 compose->msgid = NULL;
6178 if (compose->remove_references == FALSE) {
6180 if (compose->inreplyto && compose->to_list)
6181 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6184 if (compose->references)
6185 g_string_append_printf(header, "References: %s\n", compose->references);
6189 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6192 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6195 if (compose->account->organization &&
6196 strlen(compose->account->organization) &&
6197 !IS_IN_CUSTOM_HEADER("Organization")) {
6198 compose_convert_header(compose, buf, sizeof(buf),
6199 compose->account->organization,
6200 strlen("Organization: "), FALSE);
6201 g_string_append_printf(header, "Organization: %s\n", buf);
6204 /* Program version and system info */
6205 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6206 !compose->newsgroup_list) {
6207 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6209 gtk_major_version, gtk_minor_version, gtk_micro_version,
6212 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6213 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6215 gtk_major_version, gtk_minor_version, gtk_micro_version,
6219 /* custom headers */
6220 if (compose->account->add_customhdr) {
6223 for (cur = compose->account->customhdr_list; cur != NULL;
6225 CustomHeader *chdr = (CustomHeader *)cur->data;
6227 if (custom_header_is_allowed(chdr->name)) {
6228 compose_convert_header
6229 (compose, buf, sizeof(buf),
6230 chdr->value ? chdr->value : "",
6231 strlen(chdr->name) + 2, FALSE);
6232 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6237 /* Automatic Faces and X-Faces */
6238 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6239 g_string_append_printf(header, "X-Face: %s\n", buf);
6241 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6242 g_string_append_printf(header, "X-Face: %s\n", buf);
6244 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6245 g_string_append_printf(header, "Face: %s\n", buf);
6247 else if (get_default_face (buf, sizeof(buf)) == 0) {
6248 g_string_append_printf(header, "Face: %s\n", buf);
6252 switch (compose->priority) {
6253 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6254 "X-Priority: 1 (Highest)\n");
6256 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6257 "X-Priority: 2 (High)\n");
6259 case PRIORITY_NORMAL: break;
6260 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6261 "X-Priority: 4 (Low)\n");
6263 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6264 "X-Priority: 5 (Lowest)\n");
6266 default: debug_print("compose: priority unknown : %d\n",
6270 /* Request Return Receipt */
6271 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6272 if (compose->return_receipt) {
6273 if (compose->account->name
6274 && *compose->account->name) {
6275 compose_convert_header(compose, buf, sizeof(buf),
6276 compose->account->name,
6277 strlen("Disposition-Notification-To: "),
6279 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6281 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6285 /* get special headers */
6286 for (list = compose->header_list; list; list = list->next) {
6287 ComposeHeaderEntry *headerentry;
6290 gchar *headername_wcolon;
6291 const gchar *headername_trans;
6294 gboolean standard_header = FALSE;
6296 headerentry = ((ComposeHeaderEntry *)list->data);
6298 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6300 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6305 if (!strstr(tmp, ":")) {
6306 headername_wcolon = g_strconcat(tmp, ":", NULL);
6307 headername = g_strdup(tmp);
6309 headername_wcolon = g_strdup(tmp);
6310 headername = g_strdup(strtok(tmp, ":"));
6314 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6315 Xstrdup_a(headervalue, entry_str, return NULL);
6316 subst_char(headervalue, '\r', ' ');
6317 subst_char(headervalue, '\n', ' ');
6318 string = std_headers;
6319 while (*string != NULL) {
6320 headername_trans = prefs_common_translated_header_name(*string);
6321 if (!strcmp(headername_trans, headername_wcolon))
6322 standard_header = TRUE;
6325 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6326 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6329 g_free(headername_wcolon);
6333 g_string_free(header, FALSE);
6338 #undef IS_IN_CUSTOM_HEADER
6340 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6341 gint header_len, gboolean addr_field)
6343 gchar *tmpstr = NULL;
6344 const gchar *out_codeset = NULL;
6346 cm_return_if_fail(src != NULL);
6347 cm_return_if_fail(dest != NULL);
6349 if (len < 1) return;
6351 tmpstr = g_strdup(src);
6353 subst_char(tmpstr, '\n', ' ');
6354 subst_char(tmpstr, '\r', ' ');
6357 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6358 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6359 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6364 codeconv_set_strict(TRUE);
6365 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6366 conv_get_charset_str(compose->out_encoding));
6367 codeconv_set_strict(FALSE);
6369 if (!dest || *dest == '\0') {
6370 gchar *test_conv_global_out = NULL;
6371 gchar *test_conv_reply = NULL;
6373 /* automatic mode. be automatic. */
6374 codeconv_set_strict(TRUE);
6376 out_codeset = conv_get_outgoing_charset_str();
6378 debug_print("trying to convert to %s\n", out_codeset);
6379 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6382 if (!test_conv_global_out && compose->orig_charset
6383 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6384 out_codeset = compose->orig_charset;
6385 debug_print("failure; trying to convert to %s\n", out_codeset);
6386 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6389 if (!test_conv_global_out && !test_conv_reply) {
6391 out_codeset = CS_INTERNAL;
6392 debug_print("finally using %s\n", out_codeset);
6394 g_free(test_conv_global_out);
6395 g_free(test_conv_reply);
6396 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6398 codeconv_set_strict(FALSE);
6403 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6407 cm_return_if_fail(user_data != NULL);
6409 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6410 g_strstrip(address);
6411 if (*address != '\0') {
6412 gchar *name = procheader_get_fromname(address);
6413 extract_address(address);
6414 addressbook_add_contact(name, address, NULL, NULL);
6419 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6421 GtkWidget *menuitem;
6424 cm_return_if_fail(menu != NULL);
6425 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6427 menuitem = gtk_separator_menu_item_new();
6428 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6429 gtk_widget_show(menuitem);
6431 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6432 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6434 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6435 g_strstrip(address);
6436 if (*address == '\0') {
6437 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6440 g_signal_connect(G_OBJECT(menuitem), "activate",
6441 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6442 gtk_widget_show(menuitem);
6445 static void compose_create_header_entry(Compose *compose)
6447 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6454 const gchar *header = NULL;
6455 ComposeHeaderEntry *headerentry;
6456 gboolean standard_header = FALSE;
6457 GtkListStore *model;
6459 #if !(GTK_CHECK_VERSION(2,12,0))
6460 GtkTooltips *tips = compose->tooltips;
6463 headerentry = g_new0(ComposeHeaderEntry, 1);
6466 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6467 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6468 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6470 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6472 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6474 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6475 COMPOSE_NEWSGROUPS);
6476 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6478 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6479 COMPOSE_FOLLOWUPTO);
6481 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6482 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6483 G_CALLBACK(compose_grab_focus_cb), compose);
6484 gtk_widget_show(combo);
6485 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6486 compose->header_nextrow, compose->header_nextrow+1,
6487 GTK_SHRINK, GTK_FILL, 0, 0);
6488 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6489 const gchar *last_header_entry = gtk_entry_get_text(
6490 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6492 while (*string != NULL) {
6493 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6494 standard_header = TRUE;
6497 if (standard_header)
6498 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6500 if (!compose->header_last || !standard_header) {
6501 switch(compose->account->protocol) {
6503 header = prefs_common_translated_header_name("Newsgroups:");
6506 header = prefs_common_translated_header_name("To:");
6511 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6513 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6514 G_CALLBACK(compose_grab_focus_cb), compose);
6516 /* Entry field with cleanup button */
6517 button = gtk_button_new();
6518 gtk_button_set_image(GTK_BUTTON(button),
6519 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6520 gtk_widget_show(button);
6521 CLAWS_SET_TIP(button,
6522 _("Delete entry contents"));
6523 entry = gtk_entry_new();
6524 gtk_widget_show(entry);
6525 CLAWS_SET_TIP(entry,
6526 _("Use <tab> to autocomplete from addressbook"));
6527 hbox = gtk_hbox_new (FALSE, 0);
6528 gtk_widget_show(hbox);
6529 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6530 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6531 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6532 compose->header_nextrow, compose->header_nextrow+1,
6533 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6535 g_signal_connect(G_OBJECT(entry), "key-press-event",
6536 G_CALLBACK(compose_headerentry_key_press_event_cb),
6538 g_signal_connect(G_OBJECT(entry), "changed",
6539 G_CALLBACK(compose_headerentry_changed_cb),
6541 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6542 G_CALLBACK(compose_grab_focus_cb), compose);
6544 g_signal_connect(G_OBJECT(button), "clicked",
6545 G_CALLBACK(compose_headerentry_button_clicked_cb),
6549 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6550 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6551 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6552 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6553 G_CALLBACK(compose_header_drag_received_cb),
6555 g_signal_connect(G_OBJECT(entry), "drag-drop",
6556 G_CALLBACK(compose_drag_drop),
6558 g_signal_connect(G_OBJECT(entry), "populate-popup",
6559 G_CALLBACK(compose_entry_popup_extend),
6562 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6564 headerentry->compose = compose;
6565 headerentry->combo = combo;
6566 headerentry->entry = entry;
6567 headerentry->button = button;
6568 headerentry->hbox = hbox;
6569 headerentry->headernum = compose->header_nextrow;
6570 headerentry->type = PREF_NONE;
6572 compose->header_nextrow++;
6573 compose->header_last = headerentry;
6574 compose->header_list =
6575 g_slist_append(compose->header_list,
6579 static void compose_add_header_entry(Compose *compose, const gchar *header,
6580 gchar *text, ComposePrefType pref_type)
6582 ComposeHeaderEntry *last_header = compose->header_last;
6583 gchar *tmp = g_strdup(text), *email;
6584 gboolean replyto_hdr;
6586 replyto_hdr = (!strcasecmp(header,
6587 prefs_common_translated_header_name("Reply-To:")) ||
6589 prefs_common_translated_header_name("Followup-To:")) ||
6591 prefs_common_translated_header_name("In-Reply-To:")));
6593 extract_address(tmp);
6594 email = g_utf8_strdown(tmp, -1);
6596 if (replyto_hdr == FALSE &&
6597 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6599 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6600 header, text, (gint) pref_type);
6606 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6607 gtk_entry_set_text(GTK_ENTRY(
6608 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6610 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6611 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6612 last_header->type = pref_type;
6614 if (replyto_hdr == FALSE)
6615 g_hash_table_insert(compose->email_hashtable, email,
6616 GUINT_TO_POINTER(1));
6623 static void compose_destroy_headerentry(Compose *compose,
6624 ComposeHeaderEntry *headerentry)
6626 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6629 extract_address(text);
6630 email = g_utf8_strdown(text, -1);
6631 g_hash_table_remove(compose->email_hashtable, email);
6635 gtk_widget_destroy(headerentry->combo);
6636 gtk_widget_destroy(headerentry->entry);
6637 gtk_widget_destroy(headerentry->button);
6638 gtk_widget_destroy(headerentry->hbox);
6639 g_free(headerentry);
6642 static void compose_remove_header_entries(Compose *compose)
6645 for (list = compose->header_list; list; list = list->next)
6646 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6648 compose->header_last = NULL;
6649 g_slist_free(compose->header_list);
6650 compose->header_list = NULL;
6651 compose->header_nextrow = 1;
6652 compose_create_header_entry(compose);
6655 static GtkWidget *compose_create_header(Compose *compose)
6657 GtkWidget *from_optmenu_hbox;
6658 GtkWidget *header_scrolledwin;
6659 GtkWidget *header_table;
6663 /* header labels and entries */
6664 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6665 gtk_widget_show(header_scrolledwin);
6666 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6668 header_table = gtk_table_new(2, 2, FALSE);
6669 gtk_widget_show(header_table);
6670 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6671 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6672 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6675 /* option menu for selecting accounts */
6676 from_optmenu_hbox = compose_account_option_menu_create(compose);
6677 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6678 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6681 compose->header_table = header_table;
6682 compose->header_list = NULL;
6683 compose->header_nextrow = count;
6685 compose_create_header_entry(compose);
6687 compose->table = NULL;
6689 return header_scrolledwin ;
6692 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6694 Compose *compose = (Compose *)data;
6695 GdkEventButton event;
6698 event.time = gtk_get_current_event_time();
6700 return attach_button_pressed(compose->attach_clist, &event, compose);
6703 static GtkWidget *compose_create_attach(Compose *compose)
6705 GtkWidget *attach_scrwin;
6706 GtkWidget *attach_clist;
6708 GtkListStore *store;
6709 GtkCellRenderer *renderer;
6710 GtkTreeViewColumn *column;
6711 GtkTreeSelection *selection;
6713 /* attachment list */
6714 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6715 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6716 GTK_POLICY_AUTOMATIC,
6717 GTK_POLICY_AUTOMATIC);
6718 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6720 store = gtk_list_store_new(N_ATTACH_COLS,
6725 G_TYPE_AUTO_POINTER,
6727 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6728 (GTK_TREE_MODEL(store)));
6729 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6730 g_object_unref(store);
6732 renderer = gtk_cell_renderer_text_new();
6733 column = gtk_tree_view_column_new_with_attributes
6734 (_("Mime type"), renderer, "text",
6735 COL_MIMETYPE, NULL);
6736 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6738 renderer = gtk_cell_renderer_text_new();
6739 column = gtk_tree_view_column_new_with_attributes
6740 (_("Size"), renderer, "text",
6742 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6744 renderer = gtk_cell_renderer_text_new();
6745 column = gtk_tree_view_column_new_with_attributes
6746 (_("Name"), renderer, "text",
6748 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6750 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6751 prefs_common.use_stripes_everywhere);
6752 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6753 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6755 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6756 G_CALLBACK(attach_selected), compose);
6757 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6758 G_CALLBACK(attach_button_pressed), compose);
6760 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6761 G_CALLBACK(popup_attach_button_pressed), compose);
6763 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6764 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6765 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6766 G_CALLBACK(popup_attach_button_pressed), compose);
6768 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6769 G_CALLBACK(attach_key_pressed), compose);
6772 gtk_drag_dest_set(attach_clist,
6773 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6774 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6775 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6776 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6777 G_CALLBACK(compose_attach_drag_received_cb),
6779 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6780 G_CALLBACK(compose_drag_drop),
6783 compose->attach_scrwin = attach_scrwin;
6784 compose->attach_clist = attach_clist;
6786 return attach_scrwin;
6789 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6790 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6792 static GtkWidget *compose_create_others(Compose *compose)
6795 GtkWidget *savemsg_checkbtn;
6796 GtkWidget *savemsg_combo;
6797 GtkWidget *savemsg_select;
6800 gchar *folderidentifier;
6802 /* Table for settings */
6803 table = gtk_table_new(3, 1, FALSE);
6804 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6805 gtk_widget_show(table);
6806 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6809 /* Save Message to folder */
6810 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6811 gtk_widget_show(savemsg_checkbtn);
6812 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6813 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6814 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6816 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6817 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6819 savemsg_combo = gtk_combo_box_entry_new_text();
6820 compose->savemsg_checkbtn = savemsg_checkbtn;
6821 compose->savemsg_combo = savemsg_combo;
6822 gtk_widget_show(savemsg_combo);
6824 if (prefs_common.compose_save_to_history)
6825 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6826 prefs_common.compose_save_to_history);
6828 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6829 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6830 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6831 G_CALLBACK(compose_grab_focus_cb), compose);
6832 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6833 folderidentifier = folder_item_get_identifier(account_get_special_folder
6834 (compose->account, F_OUTBOX));
6835 compose_set_save_to(compose, folderidentifier);
6836 g_free(folderidentifier);
6839 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6840 gtk_widget_show(savemsg_select);
6841 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6842 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6843 G_CALLBACK(compose_savemsg_select_cb),
6851 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6853 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6854 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6857 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6862 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6865 path = folder_item_get_identifier(dest);
6867 compose_set_save_to(compose, path);
6871 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6872 GdkAtom clip, GtkTextIter *insert_place);
6875 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6879 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6881 if (event->button == 3) {
6883 GtkTextIter sel_start, sel_end;
6884 gboolean stuff_selected;
6886 /* move the cursor to allow GtkAspell to check the word
6887 * under the mouse */
6888 if (event->x && event->y) {
6889 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6890 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6892 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6895 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6896 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6899 stuff_selected = gtk_text_buffer_get_selection_bounds(
6901 &sel_start, &sel_end);
6903 gtk_text_buffer_place_cursor (buffer, &iter);
6904 /* reselect stuff */
6906 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6907 gtk_text_buffer_select_range(buffer,
6908 &sel_start, &sel_end);
6910 return FALSE; /* pass the event so that the right-click goes through */
6913 if (event->button == 2) {
6918 /* get the middle-click position to paste at the correct place */
6919 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6920 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6922 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6925 entry_paste_clipboard(compose, text,
6926 prefs_common.linewrap_pastes,
6927 GDK_SELECTION_PRIMARY, &iter);
6935 static void compose_spell_menu_changed(void *data)
6937 Compose *compose = (Compose *)data;
6939 GtkWidget *menuitem;
6940 GtkWidget *parent_item;
6941 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6944 if (compose->gtkaspell == NULL)
6947 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6948 "/Menu/Spelling/Options");
6950 /* setting the submenu removes /Spelling/Options from the factory
6951 * so we need to save it */
6953 if (parent_item == NULL) {
6954 parent_item = compose->aspell_options_menu;
6955 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6957 compose->aspell_options_menu = parent_item;
6959 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6961 spell_menu = g_slist_reverse(spell_menu);
6962 for (items = spell_menu;
6963 items; items = items->next) {
6964 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6965 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6966 gtk_widget_show(GTK_WIDGET(menuitem));
6968 g_slist_free(spell_menu);
6970 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6971 gtk_widget_show(parent_item);
6974 static void compose_dict_changed(void *data)
6976 Compose *compose = (Compose *) data;
6978 if(compose->gtkaspell &&
6979 compose->gtkaspell->recheck_when_changing_dict == FALSE)
6982 gtkaspell_highlight_all(compose->gtkaspell);
6983 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6987 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6989 Compose *compose = (Compose *)data;
6990 GdkEventButton event;
6993 event.time = gtk_get_current_event_time();
6997 return text_clicked(compose->text, &event, compose);
7000 static gboolean compose_force_window_origin = TRUE;
7001 static Compose *compose_create(PrefsAccount *account,
7010 GtkWidget *handlebox;
7012 GtkWidget *notebook;
7014 GtkWidget *attach_hbox;
7015 GtkWidget *attach_lab1;
7016 GtkWidget *attach_lab2;
7021 GtkWidget *subject_hbox;
7022 GtkWidget *subject_frame;
7023 GtkWidget *subject_entry;
7027 GtkWidget *edit_vbox;
7028 GtkWidget *ruler_hbox;
7030 GtkWidget *scrolledwin;
7032 GtkTextBuffer *buffer;
7033 GtkClipboard *clipboard;
7036 UndoMain *undostruct;
7038 gchar *titles[N_ATTACH_COLS];
7039 GtkWidget *popupmenu;
7040 GtkWidget *tmpl_menu;
7041 GtkActionGroup *action_group = NULL;
7044 GtkAspell * gtkaspell = NULL;
7047 static GdkGeometry geometry;
7049 cm_return_val_if_fail(account != NULL, NULL);
7051 debug_print("Creating compose window...\n");
7052 compose = g_new0(Compose, 1);
7054 titles[COL_MIMETYPE] = _("MIME type");
7055 titles[COL_SIZE] = _("Size");
7056 titles[COL_NAME] = _("Name");
7058 compose->batch = batch;
7059 compose->account = account;
7060 compose->folder = folder;
7062 compose->mutex = g_mutex_new();
7063 compose->set_cursor_pos = -1;
7065 #if !(GTK_CHECK_VERSION(2,12,0))
7066 compose->tooltips = tips;
7069 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7071 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7072 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7074 if (!geometry.max_width) {
7075 geometry.max_width = gdk_screen_width();
7076 geometry.max_height = gdk_screen_height();
7079 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7080 &geometry, GDK_HINT_MAX_SIZE);
7081 if (!geometry.min_width) {
7082 geometry.min_width = 600;
7083 geometry.min_height = 440;
7085 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7086 &geometry, GDK_HINT_MIN_SIZE);
7088 #ifndef GENERIC_UMPC
7089 if (compose_force_window_origin)
7090 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7091 prefs_common.compose_y);
7093 g_signal_connect(G_OBJECT(window), "delete_event",
7094 G_CALLBACK(compose_delete_cb), compose);
7095 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7096 gtk_widget_realize(window);
7098 gtkut_widget_set_composer_icon(window);
7100 vbox = gtk_vbox_new(FALSE, 0);
7101 gtk_container_add(GTK_CONTAINER(window), vbox);
7103 compose->ui_manager = gtk_ui_manager_new();
7104 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7105 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7106 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7107 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7108 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7109 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7110 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7111 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7112 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7113 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7118 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7121 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7127 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7131 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7134 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_1, "Options/Encoding/Western/"CS_ISO_8859_1, GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_15, "Options/Encoding/Western/"CS_ISO_8859_15, GTK_UI_MANAGER_MENUITEM)
7235 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_13, "Options/Encoding/Baltic/"CS_ISO_8859_13, GTK_UI_MANAGER_MENUITEM)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_4, "Options/Encoding/Baltic/"CS_ISO_8859_4, GTK_UI_MANAGER_MENUITEM)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_ISO_8859_8, "Options/Encoding/Hebrew/"CS_ISO_8859_8, GTK_UI_MANAGER_MENUITEM)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_ISO_8859_6, "Options/Encoding/Arabic/"CS_ISO_8859_6, GTK_UI_MANAGER_MENUITEM)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_ISO_8859_5, "Options/Encoding/Cyrillic/"CS_ISO_8859_5, GTK_UI_MANAGER_MENUITEM)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP, "Options/Encoding/Japanese/"CS_ISO_2022_JP, GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP_2, "Options/Encoding/Japanese/"CS_ISO_2022_JP_2, GTK_UI_MANAGER_MENUITEM)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7268 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7274 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_ISO_2022_KR, "Options/Encoding/Korean/"CS_ISO_2022_KR, GTK_UI_MANAGER_MENUITEM)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7280 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7294 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7295 gtk_widget_show_all(menubar);
7297 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7299 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7301 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7304 if (prefs_common.toolbar_detachable) {
7305 handlebox = gtk_handle_box_new();
7307 handlebox = gtk_hbox_new(FALSE, 0);
7309 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7311 gtk_widget_realize(handlebox);
7313 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7316 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7320 vbox2 = gtk_vbox_new(FALSE, 2);
7321 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7322 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7325 notebook = gtk_notebook_new();
7326 gtk_widget_set_size_request(notebook, -1, 130);
7327 gtk_widget_show(notebook);
7329 /* header labels and entries */
7330 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7331 compose_create_header(compose),
7332 gtk_label_new_with_mnemonic(_("Hea_der")));
7333 /* attachment list */
7334 attach_hbox = gtk_hbox_new(FALSE, 0);
7335 gtk_widget_show(attach_hbox);
7337 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7338 gtk_widget_show(attach_lab1);
7339 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7341 attach_lab2 = gtk_label_new("");
7342 gtk_widget_show(attach_lab2);
7343 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7345 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7346 compose_create_attach(compose),
7349 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7350 compose_create_others(compose),
7351 gtk_label_new_with_mnemonic(_("Othe_rs")));
7354 subject_hbox = gtk_hbox_new(FALSE, 0);
7355 gtk_widget_show(subject_hbox);
7357 subject_frame = gtk_frame_new(NULL);
7358 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7359 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7360 gtk_widget_show(subject_frame);
7362 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7363 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7364 gtk_widget_show(subject);
7366 label = gtk_label_new(_("Subject:"));
7367 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7368 gtk_widget_show(label);
7371 subject_entry = claws_spell_entry_new();
7373 subject_entry = gtk_entry_new();
7375 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7376 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7377 G_CALLBACK(compose_grab_focus_cb), compose);
7378 gtk_widget_show(subject_entry);
7379 compose->subject_entry = subject_entry;
7380 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7382 edit_vbox = gtk_vbox_new(FALSE, 0);
7384 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7387 ruler_hbox = gtk_hbox_new(FALSE, 0);
7388 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7390 ruler = gtk_shruler_new();
7391 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7392 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7396 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7397 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7398 GTK_POLICY_AUTOMATIC,
7399 GTK_POLICY_AUTOMATIC);
7400 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7402 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7403 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7405 text = gtk_text_view_new();
7406 if (prefs_common.show_compose_margin) {
7407 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7408 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7410 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7411 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7412 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7413 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7414 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7416 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7418 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7419 G_CALLBACK(compose_edit_size_alloc),
7421 g_signal_connect(G_OBJECT(buffer), "changed",
7422 G_CALLBACK(compose_changed_cb), compose);
7423 g_signal_connect(G_OBJECT(text), "grab_focus",
7424 G_CALLBACK(compose_grab_focus_cb), compose);
7425 g_signal_connect(G_OBJECT(buffer), "insert_text",
7426 G_CALLBACK(text_inserted), compose);
7427 g_signal_connect(G_OBJECT(text), "button_press_event",
7428 G_CALLBACK(text_clicked), compose);
7430 g_signal_connect(G_OBJECT(text), "popup-menu",
7431 G_CALLBACK(compose_popup_menu), compose);
7433 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7434 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7435 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7436 G_CALLBACK(compose_popup_menu), compose);
7438 g_signal_connect(G_OBJECT(subject_entry), "changed",
7439 G_CALLBACK(compose_changed_cb), compose);
7442 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7443 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7444 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7445 g_signal_connect(G_OBJECT(text), "drag_data_received",
7446 G_CALLBACK(compose_insert_drag_received_cb),
7448 g_signal_connect(G_OBJECT(text), "drag-drop",
7449 G_CALLBACK(compose_drag_drop),
7451 gtk_widget_show_all(vbox);
7453 /* pane between attach clist and text */
7454 paned = gtk_vpaned_new();
7455 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7457 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7458 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7460 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7462 gtk_paned_add1(GTK_PANED(paned), notebook);
7463 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7464 gtk_widget_show_all(paned);
7467 if (prefs_common.textfont) {
7468 PangoFontDescription *font_desc;
7470 font_desc = pango_font_description_from_string
7471 (prefs_common.textfont);
7473 gtk_widget_modify_font(text, font_desc);
7474 pango_font_description_free(font_desc);
7478 gtk_action_group_add_actions(action_group, compose_popup_entries,
7479 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7480 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7481 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7484 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7485 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7487 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7489 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7490 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7491 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7493 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7495 undostruct = undo_init(text);
7496 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7499 address_completion_start(window);
7501 compose->window = window;
7502 compose->vbox = vbox;
7503 compose->menubar = menubar;
7504 compose->handlebox = handlebox;
7506 compose->vbox2 = vbox2;
7508 compose->paned = paned;
7510 compose->attach_label = attach_lab2;
7512 compose->notebook = notebook;
7513 compose->edit_vbox = edit_vbox;
7514 compose->ruler_hbox = ruler_hbox;
7515 compose->ruler = ruler;
7516 compose->scrolledwin = scrolledwin;
7517 compose->text = text;
7519 compose->focused_editable = NULL;
7521 compose->popupmenu = popupmenu;
7523 compose->tmpl_menu = tmpl_menu;
7525 compose->mode = mode;
7526 compose->rmode = mode;
7528 compose->targetinfo = NULL;
7529 compose->replyinfo = NULL;
7530 compose->fwdinfo = NULL;
7532 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7533 g_str_equal, (GDestroyNotify) g_free, NULL);
7535 compose->replyto = NULL;
7537 compose->bcc = NULL;
7538 compose->followup_to = NULL;
7540 compose->ml_post = NULL;
7542 compose->inreplyto = NULL;
7543 compose->references = NULL;
7544 compose->msgid = NULL;
7545 compose->boundary = NULL;
7547 compose->autowrap = prefs_common.autowrap;
7548 compose->autoindent = prefs_common.auto_indent;
7549 compose->use_signing = FALSE;
7550 compose->use_encryption = FALSE;
7551 compose->privacy_system = NULL;
7553 compose->modified = FALSE;
7555 compose->return_receipt = FALSE;
7557 compose->to_list = NULL;
7558 compose->newsgroup_list = NULL;
7560 compose->undostruct = undostruct;
7562 compose->sig_str = NULL;
7564 compose->exteditor_file = NULL;
7565 compose->exteditor_pid = -1;
7566 compose->exteditor_tag = -1;
7567 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7570 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7571 if (mode != COMPOSE_REDIRECT) {
7572 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7573 strcmp(prefs_common.dictionary, "")) {
7574 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7575 prefs_common.alt_dictionary,
7576 conv_get_locale_charset_str(),
7577 prefs_common.misspelled_col,
7578 prefs_common.check_while_typing,
7579 prefs_common.recheck_when_changing_dict,
7580 prefs_common.use_alternate,
7581 prefs_common.use_both_dicts,
7582 GTK_TEXT_VIEW(text),
7583 GTK_WINDOW(compose->window),
7584 compose_dict_changed,
7585 compose_spell_menu_changed,
7588 alertpanel_error(_("Spell checker could not "
7590 gtkaspell_checkers_strerror());
7591 gtkaspell_checkers_reset_error();
7593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7597 compose->gtkaspell = gtkaspell;
7598 compose_spell_menu_changed(compose);
7599 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7602 compose_select_account(compose, account, TRUE);
7604 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7605 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7607 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7608 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7610 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7611 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7613 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7614 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7616 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7617 if (account->protocol != A_NNTP)
7618 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7619 prefs_common_translated_header_name("To:"));
7621 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7622 prefs_common_translated_header_name("Newsgroups:"));
7624 addressbook_set_target_compose(compose);
7626 if (mode != COMPOSE_REDIRECT)
7627 compose_set_template_menu(compose);
7629 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7632 compose_list = g_list_append(compose_list, compose);
7634 if (!prefs_common.show_ruler)
7635 gtk_widget_hide(ruler_hbox);
7637 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7640 compose->priority = PRIORITY_NORMAL;
7641 compose_update_priority_menu_item(compose);
7643 compose_set_out_encoding(compose);
7646 compose_update_actions_menu(compose);
7648 /* Privacy Systems menu */
7649 compose_update_privacy_systems_menu(compose);
7651 activate_privacy_system(compose, account, TRUE);
7652 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7654 gtk_widget_realize(window);
7656 gtk_widget_show(window);
7658 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7659 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7666 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7671 GtkWidget *optmenubox;
7674 GtkWidget *from_name = NULL;
7675 #if !(GTK_CHECK_VERSION(2,12,0))
7676 GtkTooltips *tips = compose->tooltips;
7679 gint num = 0, def_menu = 0;
7681 accounts = account_get_list();
7682 cm_return_val_if_fail(accounts != NULL, NULL);
7684 optmenubox = gtk_event_box_new();
7685 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7686 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7688 hbox = gtk_hbox_new(FALSE, 6);
7689 from_name = gtk_entry_new();
7691 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7692 G_CALLBACK(compose_grab_focus_cb), compose);
7694 for (; accounts != NULL; accounts = accounts->next, num++) {
7695 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7696 gchar *name, *from = NULL;
7698 if (ac == compose->account) def_menu = num;
7700 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7703 if (ac == compose->account) {
7704 if (ac->name && *ac->name) {
7706 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7707 from = g_strdup_printf("%s <%s>",
7709 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7711 from = g_strdup_printf("%s",
7713 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7716 COMBOBOX_ADD(menu, name, ac->account_id);
7721 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7723 g_signal_connect(G_OBJECT(optmenu), "changed",
7724 G_CALLBACK(account_activated),
7726 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7727 G_CALLBACK(compose_entry_popup_extend),
7730 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7731 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7733 CLAWS_SET_TIP(optmenubox,
7734 _("Account to use for this email"));
7735 CLAWS_SET_TIP(from_name,
7736 _("Sender address to be used"));
7738 compose->account_combo = optmenu;
7739 compose->from_name = from_name;
7744 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7746 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7747 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7748 Compose *compose = (Compose *) data;
7750 compose->priority = value;
7754 static void compose_reply_change_mode(Compose *compose,
7757 gboolean was_modified = compose->modified;
7759 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7761 cm_return_if_fail(compose->replyinfo != NULL);
7763 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7765 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7767 if (action == COMPOSE_REPLY_TO_ALL)
7769 if (action == COMPOSE_REPLY_TO_SENDER)
7771 if (action == COMPOSE_REPLY_TO_LIST)
7774 compose_remove_header_entries(compose);
7775 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7776 if (compose->account->set_autocc && compose->account->auto_cc)
7777 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7779 if (compose->account->set_autobcc && compose->account->auto_bcc)
7780 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7782 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7783 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7784 compose_show_first_last_header(compose, TRUE);
7785 compose->modified = was_modified;
7786 compose_set_title(compose);
7789 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7791 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7792 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7793 Compose *compose = (Compose *) data;
7796 compose_reply_change_mode(compose, value);
7799 static void compose_update_priority_menu_item(Compose * compose)
7801 GtkWidget *menuitem = NULL;
7802 switch (compose->priority) {
7803 case PRIORITY_HIGHEST:
7804 menuitem = gtk_ui_manager_get_widget
7805 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7808 menuitem = gtk_ui_manager_get_widget
7809 (compose->ui_manager, "/Menu/Options/Priority/High");
7811 case PRIORITY_NORMAL:
7812 menuitem = gtk_ui_manager_get_widget
7813 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7816 menuitem = gtk_ui_manager_get_widget
7817 (compose->ui_manager, "/Menu/Options/Priority/Low");
7819 case PRIORITY_LOWEST:
7820 menuitem = gtk_ui_manager_get_widget
7821 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7824 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7827 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7829 Compose *compose = (Compose *) data;
7831 gboolean can_sign = FALSE, can_encrypt = FALSE;
7833 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7835 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7838 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7839 g_free(compose->privacy_system);
7840 compose->privacy_system = NULL;
7841 if (systemid != NULL) {
7842 compose->privacy_system = g_strdup(systemid);
7844 can_sign = privacy_system_can_sign(systemid);
7845 can_encrypt = privacy_system_can_encrypt(systemid);
7848 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7850 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7851 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7854 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7856 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7857 GtkWidget *menuitem = NULL;
7859 gboolean can_sign = FALSE, can_encrypt = FALSE;
7860 gboolean found = FALSE;
7862 if (compose->privacy_system != NULL) {
7864 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7865 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7866 cm_return_if_fail(menuitem != NULL);
7868 amenu = GTK_MENU_SHELL(menuitem)->children;
7870 while (amenu != NULL) {
7871 GList *alist = amenu->next;
7873 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7874 if (systemid != NULL) {
7875 if (strcmp(systemid, compose->privacy_system) == 0 &&
7876 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7877 menuitem = GTK_WIDGET(amenu->data);
7879 can_sign = privacy_system_can_sign(systemid);
7880 can_encrypt = privacy_system_can_encrypt(systemid);
7884 } else if (strlen(compose->privacy_system) == 0 &&
7885 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7886 menuitem = GTK_WIDGET(amenu->data);
7889 can_encrypt = FALSE;
7896 if (menuitem != NULL)
7897 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7899 if (warn && !found && strlen(compose->privacy_system)) {
7900 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7901 "will not be able to sign or encrypt this message."),
7902 compose->privacy_system);
7906 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7907 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7910 static void compose_set_out_encoding(Compose *compose)
7912 CharSet out_encoding;
7913 const gchar *branch = NULL;
7914 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7916 switch(out_encoding) {
7917 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7918 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7919 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7920 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7921 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7922 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7923 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7924 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7925 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7926 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7927 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7928 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7929 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7930 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7931 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7932 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7933 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7934 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7935 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7936 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7937 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7938 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7939 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7940 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7941 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7942 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7943 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7944 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7945 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7946 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7947 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7948 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7949 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7951 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7954 static void compose_set_template_menu(Compose *compose)
7956 GSList *tmpl_list, *cur;
7960 tmpl_list = template_get_config();
7962 menu = gtk_menu_new();
7964 gtk_menu_set_accel_group (GTK_MENU (menu),
7965 gtk_ui_manager_get_accel_group(compose->ui_manager));
7966 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7967 Template *tmpl = (Template *)cur->data;
7968 gchar *accel_path = NULL;
7969 item = gtk_menu_item_new_with_label(tmpl->name);
7970 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7971 g_signal_connect(G_OBJECT(item), "activate",
7972 G_CALLBACK(compose_template_activate_cb),
7974 g_object_set_data(G_OBJECT(item), "template", tmpl);
7975 gtk_widget_show(item);
7976 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7977 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7981 gtk_widget_show(menu);
7982 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7985 void compose_update_actions_menu(Compose *compose)
7987 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7990 static void compose_update_privacy_systems_menu(Compose *compose)
7992 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7993 GSList *systems, *cur;
7995 GtkWidget *system_none;
7997 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7998 GtkWidget *privacy_menu = gtk_menu_new();
8000 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8001 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8003 g_signal_connect(G_OBJECT(system_none), "activate",
8004 G_CALLBACK(compose_set_privacy_system_cb), compose);
8006 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8007 gtk_widget_show(system_none);
8009 systems = privacy_get_system_ids();
8010 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8011 gchar *systemid = cur->data;
8013 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8014 widget = gtk_radio_menu_item_new_with_label(group,
8015 privacy_system_get_name(systemid));
8016 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8017 g_strdup(systemid), g_free);
8018 g_signal_connect(G_OBJECT(widget), "activate",
8019 G_CALLBACK(compose_set_privacy_system_cb), compose);
8021 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8022 gtk_widget_show(widget);
8025 g_slist_free(systems);
8026 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8027 gtk_widget_show_all(privacy_menu);
8028 gtk_widget_show_all(privacy_menuitem);
8031 void compose_reflect_prefs_all(void)
8036 for (cur = compose_list; cur != NULL; cur = cur->next) {
8037 compose = (Compose *)cur->data;
8038 compose_set_template_menu(compose);
8042 void compose_reflect_prefs_pixmap_theme(void)
8047 for (cur = compose_list; cur != NULL; cur = cur->next) {
8048 compose = (Compose *)cur->data;
8049 toolbar_update(TOOLBAR_COMPOSE, compose);
8053 static const gchar *compose_quote_char_from_context(Compose *compose)
8055 const gchar *qmark = NULL;
8057 cm_return_val_if_fail(compose != NULL, NULL);
8059 switch (compose->mode) {
8060 /* use forward-specific quote char */
8061 case COMPOSE_FORWARD:
8062 case COMPOSE_FORWARD_AS_ATTACH:
8063 case COMPOSE_FORWARD_INLINE:
8064 if (compose->folder && compose->folder->prefs &&
8065 compose->folder->prefs->forward_with_format)
8066 qmark = compose->folder->prefs->forward_quotemark;
8067 else if (compose->account->forward_with_format)
8068 qmark = compose->account->forward_quotemark;
8070 qmark = prefs_common.fw_quotemark;
8073 /* use reply-specific quote char in all other modes */
8075 if (compose->folder && compose->folder->prefs &&
8076 compose->folder->prefs->reply_with_format)
8077 qmark = compose->folder->prefs->reply_quotemark;
8078 else if (compose->account->reply_with_format)
8079 qmark = compose->account->reply_quotemark;
8081 qmark = prefs_common.quotemark;
8085 if (qmark == NULL || *qmark == '\0')
8091 static void compose_template_apply(Compose *compose, Template *tmpl,
8095 GtkTextBuffer *buffer;
8099 gchar *parsed_str = NULL;
8100 gint cursor_pos = 0;
8101 const gchar *err_msg = _("The body of the template has an error at line %d.");
8104 /* process the body */
8106 text = GTK_TEXT_VIEW(compose->text);
8107 buffer = gtk_text_view_get_buffer(text);
8110 qmark = compose_quote_char_from_context(compose);
8112 if (compose->replyinfo != NULL) {
8115 gtk_text_buffer_set_text(buffer, "", -1);
8116 mark = gtk_text_buffer_get_insert(buffer);
8117 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8119 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8120 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8122 } else if (compose->fwdinfo != NULL) {
8125 gtk_text_buffer_set_text(buffer, "", -1);
8126 mark = gtk_text_buffer_get_insert(buffer);
8127 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8129 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8130 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8133 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8135 GtkTextIter start, end;
8138 gtk_text_buffer_get_start_iter(buffer, &start);
8139 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8140 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8142 /* clear the buffer now */
8144 gtk_text_buffer_set_text(buffer, "", -1);
8146 parsed_str = compose_quote_fmt(compose, dummyinfo,
8147 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8148 procmsg_msginfo_free( dummyinfo );
8154 gtk_text_buffer_set_text(buffer, "", -1);
8155 mark = gtk_text_buffer_get_insert(buffer);
8156 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8159 if (replace && parsed_str && compose->account->auto_sig)
8160 compose_insert_sig(compose, FALSE);
8162 if (replace && parsed_str) {
8163 gtk_text_buffer_get_start_iter(buffer, &iter);
8164 gtk_text_buffer_place_cursor(buffer, &iter);
8168 cursor_pos = quote_fmt_get_cursor_pos();
8169 compose->set_cursor_pos = cursor_pos;
8170 if (cursor_pos == -1)
8172 gtk_text_buffer_get_start_iter(buffer, &iter);
8173 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8174 gtk_text_buffer_place_cursor(buffer, &iter);
8177 /* process the other fields */
8179 compose_template_apply_fields(compose, tmpl);
8180 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8181 quote_fmt_reset_vartable();
8182 compose_changed_cb(NULL, compose);
8185 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8186 gtkaspell_highlight_all(compose->gtkaspell);
8190 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8192 MsgInfo* dummyinfo = NULL;
8193 MsgInfo *msginfo = NULL;
8196 if (compose->replyinfo != NULL)
8197 msginfo = compose->replyinfo;
8198 else if (compose->fwdinfo != NULL)
8199 msginfo = compose->fwdinfo;
8201 dummyinfo = compose_msginfo_new_from_compose(compose);
8202 msginfo = dummyinfo;
8205 if (tmpl->from && *tmpl->from != '\0') {
8207 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8208 compose->gtkaspell);
8210 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8212 quote_fmt_scan_string(tmpl->from);
8215 buf = quote_fmt_get_buffer();
8217 alertpanel_error(_("Template From format error."));
8219 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8223 if (tmpl->to && *tmpl->to != '\0') {
8225 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8226 compose->gtkaspell);
8228 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8230 quote_fmt_scan_string(tmpl->to);
8233 buf = quote_fmt_get_buffer();
8235 alertpanel_error(_("Template To format error."));
8237 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8241 if (tmpl->cc && *tmpl->cc != '\0') {
8243 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8244 compose->gtkaspell);
8246 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8248 quote_fmt_scan_string(tmpl->cc);
8251 buf = quote_fmt_get_buffer();
8253 alertpanel_error(_("Template Cc format error."));
8255 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8259 if (tmpl->bcc && *tmpl->bcc != '\0') {
8261 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8262 compose->gtkaspell);
8264 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8266 quote_fmt_scan_string(tmpl->bcc);
8269 buf = quote_fmt_get_buffer();
8271 alertpanel_error(_("Template Bcc format error."));
8273 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8277 /* process the subject */
8278 if (tmpl->subject && *tmpl->subject != '\0') {
8280 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8281 compose->gtkaspell);
8283 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8285 quote_fmt_scan_string(tmpl->subject);
8288 buf = quote_fmt_get_buffer();
8290 alertpanel_error(_("Template subject format error."));
8292 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8296 procmsg_msginfo_free( dummyinfo );
8299 static void compose_destroy(Compose *compose)
8301 GtkTextBuffer *buffer;
8302 GtkClipboard *clipboard;
8304 compose_list = g_list_remove(compose_list, compose);
8306 if (compose->updating) {
8307 debug_print("danger, not destroying anything now\n");
8308 compose->deferred_destroy = TRUE;
8311 /* NOTE: address_completion_end() does nothing with the window
8312 * however this may change. */
8313 address_completion_end(compose->window);
8315 slist_free_strings(compose->to_list);
8316 g_slist_free(compose->to_list);
8317 slist_free_strings(compose->newsgroup_list);
8318 g_slist_free(compose->newsgroup_list);
8319 slist_free_strings(compose->header_list);
8320 g_slist_free(compose->header_list);
8322 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8324 g_hash_table_destroy(compose->email_hashtable);
8326 procmsg_msginfo_free(compose->targetinfo);
8327 procmsg_msginfo_free(compose->replyinfo);
8328 procmsg_msginfo_free(compose->fwdinfo);
8330 g_free(compose->replyto);
8331 g_free(compose->cc);
8332 g_free(compose->bcc);
8333 g_free(compose->newsgroups);
8334 g_free(compose->followup_to);
8336 g_free(compose->ml_post);
8338 g_free(compose->inreplyto);
8339 g_free(compose->references);
8340 g_free(compose->msgid);
8341 g_free(compose->boundary);
8343 g_free(compose->redirect_filename);
8344 if (compose->undostruct)
8345 undo_destroy(compose->undostruct);
8347 g_free(compose->sig_str);
8349 g_free(compose->exteditor_file);
8351 g_free(compose->orig_charset);
8353 g_free(compose->privacy_system);
8355 if (addressbook_get_target_compose() == compose)
8356 addressbook_set_target_compose(NULL);
8359 if (compose->gtkaspell) {
8360 gtkaspell_delete(compose->gtkaspell);
8361 compose->gtkaspell = NULL;
8365 if (!compose->batch) {
8366 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8367 prefs_common.compose_height = compose->window->allocation.height;
8370 if (!gtk_widget_get_parent(compose->paned))
8371 gtk_widget_destroy(compose->paned);
8372 gtk_widget_destroy(compose->popupmenu);
8374 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8375 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8376 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8378 gtk_widget_destroy(compose->window);
8379 toolbar_destroy(compose->toolbar);
8380 g_free(compose->toolbar);
8381 g_mutex_free(compose->mutex);
8385 static void compose_attach_info_free(AttachInfo *ainfo)
8387 g_free(ainfo->file);
8388 g_free(ainfo->content_type);
8389 g_free(ainfo->name);
8393 static void compose_attach_update_label(Compose *compose)
8398 GtkTreeModel *model;
8403 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8404 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8405 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8409 while(gtk_tree_model_iter_next(model, &iter))
8412 text = g_strdup_printf("(%d)", i);
8413 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8417 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8419 Compose *compose = (Compose *)data;
8420 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8421 GtkTreeSelection *selection;
8423 GtkTreeModel *model;
8425 selection = gtk_tree_view_get_selection(tree_view);
8426 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8431 for (cur = sel; cur != NULL; cur = cur->next) {
8432 GtkTreePath *path = cur->data;
8433 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8436 gtk_tree_path_free(path);
8439 for (cur = sel; cur != NULL; cur = cur->next) {
8440 GtkTreeRowReference *ref = cur->data;
8441 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8444 if (gtk_tree_model_get_iter(model, &iter, path))
8445 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8447 gtk_tree_path_free(path);
8448 gtk_tree_row_reference_free(ref);
8452 compose_attach_update_label(compose);
8455 static struct _AttachProperty
8458 GtkWidget *mimetype_entry;
8459 GtkWidget *encoding_optmenu;
8460 GtkWidget *path_entry;
8461 GtkWidget *filename_entry;
8463 GtkWidget *cancel_btn;
8466 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8468 gtk_tree_path_free((GtkTreePath *)ptr);
8471 static void compose_attach_property(GtkAction *action, gpointer data)
8473 Compose *compose = (Compose *)data;
8474 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8476 GtkComboBox *optmenu;
8477 GtkTreeSelection *selection;
8479 GtkTreeModel *model;
8482 static gboolean cancelled;
8484 /* only if one selected */
8485 selection = gtk_tree_view_get_selection(tree_view);
8486 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8489 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8493 path = (GtkTreePath *) sel->data;
8494 gtk_tree_model_get_iter(model, &iter, path);
8495 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8498 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8504 if (!attach_prop.window)
8505 compose_attach_property_create(&cancelled);
8506 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8507 gtk_widget_grab_focus(attach_prop.ok_btn);
8508 gtk_widget_show(attach_prop.window);
8509 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8511 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8512 if (ainfo->encoding == ENC_UNKNOWN)
8513 combobox_select_by_data(optmenu, ENC_BASE64);
8515 combobox_select_by_data(optmenu, ainfo->encoding);
8517 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8518 ainfo->content_type ? ainfo->content_type : "");
8519 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8520 ainfo->file ? ainfo->file : "");
8521 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8522 ainfo->name ? ainfo->name : "");
8525 const gchar *entry_text;
8527 gchar *cnttype = NULL;
8534 gtk_widget_hide(attach_prop.window);
8535 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8540 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8541 if (*entry_text != '\0') {
8544 text = g_strstrip(g_strdup(entry_text));
8545 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8546 cnttype = g_strdup(text);
8549 alertpanel_error(_("Invalid MIME type."));
8555 ainfo->encoding = combobox_get_active_data(optmenu);
8557 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8558 if (*entry_text != '\0') {
8559 if (is_file_exist(entry_text) &&
8560 (size = get_file_size(entry_text)) > 0)
8561 file = g_strdup(entry_text);
8564 (_("File doesn't exist or is empty."));
8570 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8571 if (*entry_text != '\0') {
8572 g_free(ainfo->name);
8573 ainfo->name = g_strdup(entry_text);
8577 g_free(ainfo->content_type);
8578 ainfo->content_type = cnttype;
8581 g_free(ainfo->file);
8585 ainfo->size = (goffset)size;
8587 /* update tree store */
8588 text = to_human_readable(ainfo->size);
8589 gtk_tree_model_get_iter(model, &iter, path);
8590 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8591 COL_MIMETYPE, ainfo->content_type,
8593 COL_NAME, ainfo->name,
8599 gtk_tree_path_free(path);
8602 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8604 label = gtk_label_new(str); \
8605 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8606 GTK_FILL, 0, 0, 0); \
8607 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8609 entry = gtk_entry_new(); \
8610 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8611 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8614 static void compose_attach_property_create(gboolean *cancelled)
8620 GtkWidget *mimetype_entry;
8623 GtkListStore *optmenu_menu;
8624 GtkWidget *path_entry;
8625 GtkWidget *filename_entry;
8628 GtkWidget *cancel_btn;
8629 GList *mime_type_list, *strlist;
8632 debug_print("Creating attach_property window...\n");
8634 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8635 gtk_widget_set_size_request(window, 480, -1);
8636 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8637 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8638 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8639 g_signal_connect(G_OBJECT(window), "delete_event",
8640 G_CALLBACK(attach_property_delete_event),
8642 g_signal_connect(G_OBJECT(window), "key_press_event",
8643 G_CALLBACK(attach_property_key_pressed),
8646 vbox = gtk_vbox_new(FALSE, 8);
8647 gtk_container_add(GTK_CONTAINER(window), vbox);
8649 table = gtk_table_new(4, 2, FALSE);
8650 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8651 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8652 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8654 label = gtk_label_new(_("MIME type"));
8655 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8657 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8658 mimetype_entry = gtk_combo_box_entry_new_text();
8659 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8660 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8662 /* stuff with list */
8663 mime_type_list = procmime_get_mime_type_list();
8665 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8666 MimeType *type = (MimeType *) mime_type_list->data;
8669 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8671 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8674 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8675 (GCompareFunc)strcmp2);
8678 for (mime_type_list = strlist; mime_type_list != NULL;
8679 mime_type_list = mime_type_list->next) {
8680 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8681 g_free(mime_type_list->data);
8683 g_list_free(strlist);
8684 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8685 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8687 label = gtk_label_new(_("Encoding"));
8688 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8690 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8692 hbox = gtk_hbox_new(FALSE, 0);
8693 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8694 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8696 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8697 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8699 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8700 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8701 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8702 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8703 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8705 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8707 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8708 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8710 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8711 &ok_btn, GTK_STOCK_OK,
8713 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8714 gtk_widget_grab_default(ok_btn);
8716 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8717 G_CALLBACK(attach_property_ok),
8719 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8720 G_CALLBACK(attach_property_cancel),
8723 gtk_widget_show_all(vbox);
8725 attach_prop.window = window;
8726 attach_prop.mimetype_entry = mimetype_entry;
8727 attach_prop.encoding_optmenu = optmenu;
8728 attach_prop.path_entry = path_entry;
8729 attach_prop.filename_entry = filename_entry;
8730 attach_prop.ok_btn = ok_btn;
8731 attach_prop.cancel_btn = cancel_btn;
8734 #undef SET_LABEL_AND_ENTRY
8736 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8742 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8748 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8749 gboolean *cancelled)
8757 static gboolean attach_property_key_pressed(GtkWidget *widget,
8759 gboolean *cancelled)
8761 if (event && event->keyval == GDK_Escape) {
8765 if (event && event->keyval == GDK_Return) {
8773 static void compose_exec_ext_editor(Compose *compose)
8780 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8781 G_DIR_SEPARATOR, compose);
8783 if (pipe(pipe_fds) < 0) {
8789 if ((pid = fork()) < 0) {
8796 /* close the write side of the pipe */
8799 compose->exteditor_file = g_strdup(tmp);
8800 compose->exteditor_pid = pid;
8802 compose_set_ext_editor_sensitive(compose, FALSE);
8805 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8807 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8809 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8813 } else { /* process-monitoring process */
8819 /* close the read side of the pipe */
8822 if (compose_write_body_to_file(compose, tmp) < 0) {
8823 fd_write_all(pipe_fds[1], "2\n", 2);
8827 pid_ed = compose_exec_ext_editor_real(tmp);
8829 fd_write_all(pipe_fds[1], "1\n", 2);
8833 /* wait until editor is terminated */
8834 waitpid(pid_ed, NULL, 0);
8836 fd_write_all(pipe_fds[1], "0\n", 2);
8843 #endif /* G_OS_UNIX */
8847 static gint compose_exec_ext_editor_real(const gchar *file)
8854 cm_return_val_if_fail(file != NULL, -1);
8856 if ((pid = fork()) < 0) {
8861 if (pid != 0) return pid;
8863 /* grandchild process */
8865 if (setpgid(0, getppid()))
8868 if (prefs_common_get_ext_editor_cmd() &&
8869 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8870 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8871 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8873 if (prefs_common_get_ext_editor_cmd())
8874 g_warning("External editor command-line is invalid: '%s'\n",
8875 prefs_common_get_ext_editor_cmd());
8876 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8879 cmdline = strsplit_with_quote(buf, " ", 1024);
8880 execvp(cmdline[0], cmdline);
8883 g_strfreev(cmdline);
8888 static gboolean compose_ext_editor_kill(Compose *compose)
8890 pid_t pgid = compose->exteditor_pid * -1;
8893 ret = kill(pgid, 0);
8895 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8899 msg = g_strdup_printf
8900 (_("The external editor is still working.\n"
8901 "Force terminating the process?\n"
8902 "process group id: %d"), -pgid);
8903 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8904 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8908 if (val == G_ALERTALTERNATE) {
8909 g_source_remove(compose->exteditor_tag);
8910 g_io_channel_shutdown(compose->exteditor_ch,
8912 g_io_channel_unref(compose->exteditor_ch);
8914 if (kill(pgid, SIGTERM) < 0) perror("kill");
8915 waitpid(compose->exteditor_pid, NULL, 0);
8917 g_warning("Terminated process group id: %d", -pgid);
8918 g_warning("Temporary file: %s",
8919 compose->exteditor_file);
8921 compose_set_ext_editor_sensitive(compose, TRUE);
8923 g_free(compose->exteditor_file);
8924 compose->exteditor_file = NULL;
8925 compose->exteditor_pid = -1;
8926 compose->exteditor_ch = NULL;
8927 compose->exteditor_tag = -1;
8935 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8939 Compose *compose = (Compose *)data;
8942 debug_print(_("Compose: input from monitoring process\n"));
8944 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8946 g_io_channel_shutdown(source, FALSE, NULL);
8947 g_io_channel_unref(source);
8949 waitpid(compose->exteditor_pid, NULL, 0);
8951 if (buf[0] == '0') { /* success */
8952 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8953 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8955 gtk_text_buffer_set_text(buffer, "", -1);
8956 compose_insert_file(compose, compose->exteditor_file);
8957 compose_changed_cb(NULL, compose);
8958 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8960 if (claws_unlink(compose->exteditor_file) < 0)
8961 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8962 } else if (buf[0] == '1') { /* failed */
8963 g_warning("Couldn't exec external editor\n");
8964 if (claws_unlink(compose->exteditor_file) < 0)
8965 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8966 } else if (buf[0] == '2') {
8967 g_warning("Couldn't write to file\n");
8968 } else if (buf[0] == '3') {
8969 g_warning("Pipe read failed\n");
8972 compose_set_ext_editor_sensitive(compose, TRUE);
8974 g_free(compose->exteditor_file);
8975 compose->exteditor_file = NULL;
8976 compose->exteditor_pid = -1;
8977 compose->exteditor_ch = NULL;
8978 compose->exteditor_tag = -1;
8983 static void compose_set_ext_editor_sensitive(Compose *compose,
8986 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8987 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8988 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8989 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8991 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8992 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8994 gtk_widget_set_sensitive(compose->text, sensitive);
8995 if (compose->toolbar->send_btn)
8996 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8997 if (compose->toolbar->sendl_btn)
8998 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8999 if (compose->toolbar->draft_btn)
9000 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9001 if (compose->toolbar->insert_btn)
9002 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9003 if (compose->toolbar->sig_btn)
9004 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9005 if (compose->toolbar->exteditor_btn)
9006 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9007 if (compose->toolbar->linewrap_current_btn)
9008 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9009 if (compose->toolbar->linewrap_all_btn)
9010 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9012 #endif /* G_OS_UNIX */
9015 * compose_undo_state_changed:
9017 * Change the sensivity of the menuentries undo and redo
9019 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9020 gint redo_state, gpointer data)
9022 Compose *compose = (Compose *)data;
9024 switch (undo_state) {
9025 case UNDO_STATE_TRUE:
9026 if (!undostruct->undo_state) {
9027 undostruct->undo_state = TRUE;
9028 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9031 case UNDO_STATE_FALSE:
9032 if (undostruct->undo_state) {
9033 undostruct->undo_state = FALSE;
9034 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9037 case UNDO_STATE_UNCHANGED:
9039 case UNDO_STATE_REFRESH:
9040 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9043 g_warning("Undo state not recognized");
9047 switch (redo_state) {
9048 case UNDO_STATE_TRUE:
9049 if (!undostruct->redo_state) {
9050 undostruct->redo_state = TRUE;
9051 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9054 case UNDO_STATE_FALSE:
9055 if (undostruct->redo_state) {
9056 undostruct->redo_state = FALSE;
9057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9060 case UNDO_STATE_UNCHANGED:
9062 case UNDO_STATE_REFRESH:
9063 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9066 g_warning("Redo state not recognized");
9071 /* callback functions */
9073 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9074 * includes "non-client" (windows-izm) in calculation, so this calculation
9075 * may not be accurate.
9077 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9078 GtkAllocation *allocation,
9079 GtkSHRuler *shruler)
9081 if (prefs_common.show_ruler) {
9082 gint char_width = 0, char_height = 0;
9083 gint line_width_in_chars;
9085 gtkut_get_font_size(GTK_WIDGET(widget),
9086 &char_width, &char_height);
9087 line_width_in_chars =
9088 (allocation->width - allocation->x) / char_width;
9090 /* got the maximum */
9091 gtk_ruler_set_range(GTK_RULER(shruler),
9092 0.0, line_width_in_chars, 0,
9093 /*line_width_in_chars*/ char_width);
9102 ComposePrefType type;
9103 gboolean entry_marked;
9106 static void account_activated(GtkComboBox *optmenu, gpointer data)
9108 Compose *compose = (Compose *)data;
9111 gchar *folderidentifier;
9112 gint account_id = 0;
9115 GSList *list, *saved_list = NULL;
9116 HeaderEntryState *state;
9117 GtkRcStyle *style = NULL;
9118 static GdkColor yellow;
9119 static gboolean color_set = FALSE;
9121 /* Get ID of active account in the combo box */
9122 menu = gtk_combo_box_get_model(optmenu);
9123 gtk_combo_box_get_active_iter(optmenu, &iter);
9124 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9126 ac = account_find_from_id(account_id);
9127 cm_return_if_fail(ac != NULL);
9129 if (ac != compose->account) {
9130 compose_select_account(compose, ac, FALSE);
9132 for (list = compose->header_list; list; list = list->next) {
9133 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9135 if (hentry->type == PREF_ACCOUNT || !list->next) {
9136 compose_destroy_headerentry(compose, hentry);
9140 state = g_malloc0(sizeof(HeaderEntryState));
9141 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9142 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9143 state->entry = gtk_editable_get_chars(
9144 GTK_EDITABLE(hentry->entry), 0, -1);
9145 state->type = hentry->type;
9148 gdk_color_parse("#f5f6be", &yellow);
9149 color_set = gdk_colormap_alloc_color(
9150 gdk_colormap_get_system(),
9151 &yellow, FALSE, TRUE);
9154 style = gtk_widget_get_modifier_style(hentry->entry);
9155 state->entry_marked = gdk_color_equal(&yellow,
9156 &style->base[GTK_STATE_NORMAL]);
9158 saved_list = g_slist_append(saved_list, state);
9159 compose_destroy_headerentry(compose, hentry);
9162 compose->header_last = NULL;
9163 g_slist_free(compose->header_list);
9164 compose->header_list = NULL;
9165 compose->header_nextrow = 1;
9166 compose_create_header_entry(compose);
9168 if (ac->set_autocc && ac->auto_cc)
9169 compose_entry_append(compose, ac->auto_cc,
9170 COMPOSE_CC, PREF_ACCOUNT);
9172 if (ac->set_autobcc && ac->auto_bcc)
9173 compose_entry_append(compose, ac->auto_bcc,
9174 COMPOSE_BCC, PREF_ACCOUNT);
9176 if (ac->set_autoreplyto && ac->auto_replyto)
9177 compose_entry_append(compose, ac->auto_replyto,
9178 COMPOSE_REPLYTO, PREF_ACCOUNT);
9180 for (list = saved_list; list; list = list->next) {
9181 state = (HeaderEntryState *) list->data;
9183 compose_add_header_entry(compose, state->header,
9184 state->entry, state->type);
9185 if (state->entry_marked)
9186 compose_entry_mark_default_to(compose, state->entry);
9188 g_free(state->header);
9189 g_free(state->entry);
9192 g_slist_free(saved_list);
9194 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9195 (ac->protocol == A_NNTP) ?
9196 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9199 /* Set message save folder */
9200 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9203 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9204 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9206 compose_set_save_to(compose, NULL);
9207 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9208 folderidentifier = folder_item_get_identifier(account_get_special_folder
9209 (compose->account, F_OUTBOX));
9210 compose_set_save_to(compose, folderidentifier);
9211 g_free(folderidentifier);
9215 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9216 GtkTreeViewColumn *column, Compose *compose)
9218 compose_attach_property(NULL, compose);
9221 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9224 Compose *compose = (Compose *)data;
9225 GtkTreeSelection *attach_selection;
9226 gint attach_nr_selected;
9228 if (!event) return FALSE;
9230 if (event->button == 3) {
9231 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9232 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9234 if (attach_nr_selected > 0)
9236 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9237 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9239 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9240 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9243 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9244 NULL, NULL, event->button, event->time);
9251 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9254 Compose *compose = (Compose *)data;
9256 if (!event) return FALSE;
9258 switch (event->keyval) {
9260 compose_attach_remove_selected(NULL, compose);
9266 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9268 toolbar_comp_set_sensitive(compose, allow);
9269 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9270 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9272 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9278 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9282 static void compose_send_cb(GtkAction *action, gpointer data)
9284 Compose *compose = (Compose *)data;
9286 if (prefs_common.work_offline &&
9287 !inc_offline_should_override(TRUE,
9288 _("Claws Mail needs network access in order "
9289 "to send this email.")))
9292 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9293 g_source_remove(compose->draft_timeout_tag);
9294 compose->draft_timeout_tag = -1;
9297 compose_send(compose);
9300 static void compose_send_later_cb(GtkAction *action, gpointer data)
9302 Compose *compose = (Compose *)data;
9306 compose_allow_user_actions(compose, FALSE);
9307 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9308 compose_allow_user_actions(compose, TRUE);
9312 compose_close(compose);
9313 } else if (val == -1) {
9314 alertpanel_error(_("Could not queue message."));
9315 } else if (val == -2) {
9316 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9317 } else if (val == -3) {
9318 if (privacy_peek_error())
9319 alertpanel_error(_("Could not queue message for sending:\n\n"
9320 "Signature failed: %s"), privacy_get_error());
9321 } else if (val == -4) {
9322 alertpanel_error(_("Could not queue message for sending:\n\n"
9323 "Charset conversion failed."));
9324 } else if (val == -5) {
9325 alertpanel_error(_("Could not queue message for sending:\n\n"
9326 "Couldn't get recipient encryption key."));
9327 } else if (val == -6) {
9330 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9333 #define DRAFTED_AT_EXIT "drafted_at_exit"
9334 static void compose_register_draft(MsgInfo *info)
9336 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9337 DRAFTED_AT_EXIT, NULL);
9338 FILE *fp = g_fopen(filepath, "ab");
9341 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9349 gboolean compose_draft (gpointer data, guint action)
9351 Compose *compose = (Compose *)data;
9355 MsgFlags flag = {0, 0};
9356 static gboolean lock = FALSE;
9357 MsgInfo *newmsginfo;
9359 gboolean target_locked = FALSE;
9360 gboolean err = FALSE;
9362 if (lock) return FALSE;
9364 if (compose->sending)
9367 draft = account_get_special_folder(compose->account, F_DRAFT);
9368 cm_return_val_if_fail(draft != NULL, FALSE);
9370 if (!g_mutex_trylock(compose->mutex)) {
9371 /* we don't want to lock the mutex once it's available,
9372 * because as the only other part of compose.c locking
9373 * it is compose_close - which means once unlocked,
9374 * the compose struct will be freed */
9375 debug_print("couldn't lock mutex, probably sending\n");
9381 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9382 G_DIR_SEPARATOR, compose);
9383 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9384 FILE_OP_ERROR(tmp, "fopen");
9388 /* chmod for security */
9389 if (change_file_mode_rw(fp, tmp) < 0) {
9390 FILE_OP_ERROR(tmp, "chmod");
9391 g_warning("can't change file mode\n");
9394 /* Save draft infos */
9395 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9396 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9398 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9399 gchar *savefolderid;
9401 savefolderid = compose_get_save_to(compose);
9402 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9403 g_free(savefolderid);
9405 if (compose->return_receipt) {
9406 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9408 if (compose->privacy_system) {
9409 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9410 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9411 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9414 /* Message-ID of message replying to */
9415 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9418 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9419 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9422 /* Message-ID of message forwarding to */
9423 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9426 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9427 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9431 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9432 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9434 /* end of headers */
9435 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9442 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9446 if (fclose(fp) == EOF) {
9450 if (compose->targetinfo) {
9451 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9452 flag.perm_flags = target_locked?MSG_LOCKED:0;
9454 flag.tmp_flags = MSG_DRAFT;
9456 folder_item_scan(draft);
9457 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9458 MsgInfo *tmpinfo = NULL;
9459 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9460 if (compose->msgid) {
9461 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9464 msgnum = tmpinfo->msgnum;
9465 procmsg_msginfo_free(tmpinfo);
9466 debug_print("got draft msgnum %d from scanning\n", msgnum);
9468 debug_print("didn't get draft msgnum after scanning\n");
9471 debug_print("got draft msgnum %d from adding\n", msgnum);
9477 if (action != COMPOSE_AUTO_SAVE) {
9478 if (action != COMPOSE_DRAFT_FOR_EXIT)
9479 alertpanel_error(_("Could not save draft."));
9482 gtkut_window_popup(compose->window);
9483 val = alertpanel_full(_("Could not save draft"),
9484 _("Could not save draft.\n"
9485 "Do you want to cancel exit or discard this email?"),
9486 _("_Cancel exit"), _("_Discard email"), NULL,
9487 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9488 if (val == G_ALERTALTERNATE) {
9490 g_mutex_unlock(compose->mutex); /* must be done before closing */
9491 compose_close(compose);
9495 g_mutex_unlock(compose->mutex); /* must be done before closing */
9504 if (compose->mode == COMPOSE_REEDIT) {
9505 compose_remove_reedit_target(compose, TRUE);
9508 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9511 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9513 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9515 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9516 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9517 procmsg_msginfo_set_flags(newmsginfo, 0,
9518 MSG_HAS_ATTACHMENT);
9520 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9521 compose_register_draft(newmsginfo);
9523 procmsg_msginfo_free(newmsginfo);
9526 folder_item_scan(draft);
9528 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9530 g_mutex_unlock(compose->mutex); /* must be done before closing */
9531 compose_close(compose);
9537 path = folder_item_fetch_msg(draft, msgnum);
9539 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9542 if (g_stat(path, &s) < 0) {
9543 FILE_OP_ERROR(path, "stat");
9549 procmsg_msginfo_free(compose->targetinfo);
9550 compose->targetinfo = procmsg_msginfo_new();
9551 compose->targetinfo->msgnum = msgnum;
9552 compose->targetinfo->size = (goffset)s.st_size;
9553 compose->targetinfo->mtime = s.st_mtime;
9554 compose->targetinfo->folder = draft;
9556 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9557 compose->mode = COMPOSE_REEDIT;
9559 if (action == COMPOSE_AUTO_SAVE) {
9560 compose->autosaved_draft = compose->targetinfo;
9562 compose->modified = FALSE;
9563 compose_set_title(compose);
9567 g_mutex_unlock(compose->mutex);
9571 void compose_clear_exit_drafts(void)
9573 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9574 DRAFTED_AT_EXIT, NULL);
9575 if (is_file_exist(filepath))
9576 claws_unlink(filepath);
9581 void compose_reopen_exit_drafts(void)
9583 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9584 DRAFTED_AT_EXIT, NULL);
9585 FILE *fp = g_fopen(filepath, "rb");
9589 while (fgets(buf, sizeof(buf), fp)) {
9590 gchar **parts = g_strsplit(buf, "\t", 2);
9591 const gchar *folder = parts[0];
9592 int msgnum = parts[1] ? atoi(parts[1]):-1;
9594 if (folder && *folder && msgnum > -1) {
9595 FolderItem *item = folder_find_item_from_identifier(folder);
9596 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9598 compose_reedit(info, FALSE);
9605 compose_clear_exit_drafts();
9608 static void compose_save_cb(GtkAction *action, gpointer data)
9610 Compose *compose = (Compose *)data;
9611 compose_draft(compose, COMPOSE_KEEP_EDITING);
9612 compose->rmode = COMPOSE_REEDIT;
9615 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9617 if (compose && file_list) {
9620 for ( tmp = file_list; tmp; tmp = tmp->next) {
9621 gchar *file = (gchar *) tmp->data;
9622 gchar *utf8_filename = conv_filename_to_utf8(file);
9623 compose_attach_append(compose, file, utf8_filename, NULL);
9624 compose_changed_cb(NULL, compose);
9629 g_free(utf8_filename);
9634 static void compose_attach_cb(GtkAction *action, gpointer data)
9636 Compose *compose = (Compose *)data;
9639 if (compose->redirect_filename != NULL)
9642 file_list = filesel_select_multiple_files_open(_("Select file"));
9645 compose_attach_from_list(compose, file_list, TRUE);
9646 g_list_free(file_list);
9650 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9652 Compose *compose = (Compose *)data;
9654 gint files_inserted = 0;
9656 file_list = filesel_select_multiple_files_open(_("Select file"));
9661 for ( tmp = file_list; tmp; tmp = tmp->next) {
9662 gchar *file = (gchar *) tmp->data;
9663 gchar *filedup = g_strdup(file);
9664 gchar *shortfile = g_path_get_basename(filedup);
9665 ComposeInsertResult res;
9666 /* insert the file if the file is short or if the user confirmed that
9667 he/she wants to insert the large file */
9668 res = compose_insert_file(compose, file);
9669 if (res == COMPOSE_INSERT_READ_ERROR) {
9670 alertpanel_error(_("File '%s' could not be read."), shortfile);
9671 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9672 alertpanel_error(_("File '%s' contained invalid characters\n"
9673 "for the current encoding, insertion may be incorrect."),
9675 } else if (res == COMPOSE_INSERT_SUCCESS)
9682 g_list_free(file_list);
9686 if (files_inserted > 0 && compose->gtkaspell &&
9687 compose->gtkaspell->check_while_typing)
9688 gtkaspell_highlight_all(compose->gtkaspell);
9692 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9694 Compose *compose = (Compose *)data;
9696 compose_insert_sig(compose, FALSE);
9699 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9703 Compose *compose = (Compose *)data;
9705 gtkut_widget_get_uposition(widget, &x, &y);
9706 if (!compose->batch) {
9707 prefs_common.compose_x = x;
9708 prefs_common.compose_y = y;
9710 if (compose->sending || compose->updating)
9712 compose_close_cb(NULL, compose);
9716 void compose_close_toolbar(Compose *compose)
9718 compose_close_cb(NULL, compose);
9721 static void compose_close_cb(GtkAction *action, gpointer data)
9723 Compose *compose = (Compose *)data;
9727 if (compose->exteditor_tag != -1) {
9728 if (!compose_ext_editor_kill(compose))
9733 if (compose->modified) {
9734 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9735 if (!g_mutex_trylock(compose->mutex)) {
9736 /* we don't want to lock the mutex once it's available,
9737 * because as the only other part of compose.c locking
9738 * it is compose_close - which means once unlocked,
9739 * the compose struct will be freed */
9740 debug_print("couldn't lock mutex, probably sending\n");
9744 val = alertpanel(_("Discard message"),
9745 _("This message has been modified. Discard it?"),
9746 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9748 val = alertpanel(_("Save changes"),
9749 _("This message has been modified. Save the latest changes?"),
9750 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9752 g_mutex_unlock(compose->mutex);
9754 case G_ALERTDEFAULT:
9755 if (prefs_common.autosave && !reedit)
9756 compose_remove_draft(compose);
9758 case G_ALERTALTERNATE:
9759 compose_draft(data, COMPOSE_QUIT_EDITING);
9766 compose_close(compose);
9769 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9771 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9772 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9773 Compose *compose = (Compose *) data;
9776 compose->out_encoding = (CharSet)value;
9779 static void compose_address_cb(GtkAction *action, gpointer data)
9781 Compose *compose = (Compose *)data;
9783 addressbook_open(compose);
9786 static void about_show_cb(GtkAction *action, gpointer data)
9791 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9793 Compose *compose = (Compose *)data;
9798 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9799 cm_return_if_fail(tmpl != NULL);
9801 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9803 val = alertpanel(_("Apply template"), msg,
9804 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9807 if (val == G_ALERTDEFAULT)
9808 compose_template_apply(compose, tmpl, TRUE);
9809 else if (val == G_ALERTALTERNATE)
9810 compose_template_apply(compose, tmpl, FALSE);
9813 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9815 Compose *compose = (Compose *)data;
9817 compose_exec_ext_editor(compose);
9820 static void compose_undo_cb(GtkAction *action, gpointer data)
9822 Compose *compose = (Compose *)data;
9823 gboolean prev_autowrap = compose->autowrap;
9825 compose->autowrap = FALSE;
9826 undo_undo(compose->undostruct);
9827 compose->autowrap = prev_autowrap;
9830 static void compose_redo_cb(GtkAction *action, gpointer data)
9832 Compose *compose = (Compose *)data;
9833 gboolean prev_autowrap = compose->autowrap;
9835 compose->autowrap = FALSE;
9836 undo_redo(compose->undostruct);
9837 compose->autowrap = prev_autowrap;
9840 static void entry_cut_clipboard(GtkWidget *entry)
9842 if (GTK_IS_EDITABLE(entry))
9843 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9844 else if (GTK_IS_TEXT_VIEW(entry))
9845 gtk_text_buffer_cut_clipboard(
9846 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9847 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9851 static void entry_copy_clipboard(GtkWidget *entry)
9853 if (GTK_IS_EDITABLE(entry))
9854 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9855 else if (GTK_IS_TEXT_VIEW(entry))
9856 gtk_text_buffer_copy_clipboard(
9857 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9858 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9861 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9862 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9864 if (GTK_IS_TEXT_VIEW(entry)) {
9865 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9866 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9867 GtkTextIter start_iter, end_iter;
9869 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9871 if (contents == NULL)
9874 /* we shouldn't delete the selection when middle-click-pasting, or we
9875 * can't mid-click-paste our own selection */
9876 if (clip != GDK_SELECTION_PRIMARY) {
9877 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9878 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9881 if (insert_place == NULL) {
9882 /* if insert_place isn't specified, insert at the cursor.
9883 * used for Ctrl-V pasting */
9884 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9885 start = gtk_text_iter_get_offset(&start_iter);
9886 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9888 /* if insert_place is specified, paste here.
9889 * used for mid-click-pasting */
9890 start = gtk_text_iter_get_offset(insert_place);
9891 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9892 if (prefs_common.primary_paste_unselects)
9893 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9897 /* paste unwrapped: mark the paste so it's not wrapped later */
9898 end = start + strlen(contents);
9899 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9900 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9901 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9902 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9903 /* rewrap paragraph now (after a mid-click-paste) */
9904 mark_start = gtk_text_buffer_get_insert(buffer);
9905 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9906 gtk_text_iter_backward_char(&start_iter);
9907 compose_beautify_paragraph(compose, &start_iter, TRUE);
9909 } else if (GTK_IS_EDITABLE(entry))
9910 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9912 compose->modified = TRUE;
9915 static void entry_allsel(GtkWidget *entry)
9917 if (GTK_IS_EDITABLE(entry))
9918 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9919 else if (GTK_IS_TEXT_VIEW(entry)) {
9920 GtkTextIter startiter, enditer;
9921 GtkTextBuffer *textbuf;
9923 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9924 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9925 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9927 gtk_text_buffer_move_mark_by_name(textbuf,
9928 "selection_bound", &startiter);
9929 gtk_text_buffer_move_mark_by_name(textbuf,
9930 "insert", &enditer);
9934 static void compose_cut_cb(GtkAction *action, gpointer data)
9936 Compose *compose = (Compose *)data;
9937 if (compose->focused_editable
9938 #ifndef GENERIC_UMPC
9939 && gtkut_widget_has_focus(compose->focused_editable)
9942 entry_cut_clipboard(compose->focused_editable);
9945 static void compose_copy_cb(GtkAction *action, gpointer data)
9947 Compose *compose = (Compose *)data;
9948 if (compose->focused_editable
9949 #ifndef GENERIC_UMPC
9950 && gtkut_widget_has_focus(compose->focused_editable)
9953 entry_copy_clipboard(compose->focused_editable);
9956 static void compose_paste_cb(GtkAction *action, gpointer data)
9958 Compose *compose = (Compose *)data;
9960 GtkTextBuffer *buffer;
9962 if (compose->focused_editable &&
9963 gtkut_widget_has_focus(compose->focused_editable))
9964 entry_paste_clipboard(compose, compose->focused_editable,
9965 prefs_common.linewrap_pastes,
9966 GDK_SELECTION_CLIPBOARD, NULL);
9970 if (gtkut_widget_has_focus(compose->text) &&
9971 compose->gtkaspell &&
9972 compose->gtkaspell->check_while_typing)
9973 gtkaspell_highlight_all(compose->gtkaspell);
9977 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9979 Compose *compose = (Compose *)data;
9980 gint wrap_quote = prefs_common.linewrap_quote;
9981 if (compose->focused_editable
9982 #ifndef GENERIC_UMPC
9983 && gtkut_widget_has_focus(compose->focused_editable)
9986 /* let text_insert() (called directly or at a later time
9987 * after the gtk_editable_paste_clipboard) know that
9988 * text is to be inserted as a quotation. implemented
9989 * by using a simple refcount... */
9990 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9991 G_OBJECT(compose->focused_editable),
9992 "paste_as_quotation"));
9993 g_object_set_data(G_OBJECT(compose->focused_editable),
9994 "paste_as_quotation",
9995 GINT_TO_POINTER(paste_as_quotation + 1));
9996 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9997 entry_paste_clipboard(compose, compose->focused_editable,
9998 prefs_common.linewrap_pastes,
9999 GDK_SELECTION_CLIPBOARD, NULL);
10000 prefs_common.linewrap_quote = wrap_quote;
10004 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10006 Compose *compose = (Compose *)data;
10007 gint prev_autowrap;
10008 GtkTextBuffer *buffer;
10010 if (compose->focused_editable
10011 #ifndef GENERIC_UMPC
10012 && gtkut_widget_has_focus(compose->focused_editable)
10015 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10016 GDK_SELECTION_CLIPBOARD, NULL);
10020 if (gtkut_widget_has_focus(compose->text) &&
10021 compose->gtkaspell &&
10022 compose->gtkaspell->check_while_typing)
10023 gtkaspell_highlight_all(compose->gtkaspell);
10027 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10029 Compose *compose = (Compose *)data;
10030 gint prev_autowrap;
10031 GtkTextBuffer *buffer;
10033 if (compose->focused_editable
10034 #ifndef GENERIC_UMPC
10035 && gtkut_widget_has_focus(compose->focused_editable)
10038 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10039 GDK_SELECTION_CLIPBOARD, NULL);
10043 if (gtkut_widget_has_focus(compose->text) &&
10044 compose->gtkaspell &&
10045 compose->gtkaspell->check_while_typing)
10046 gtkaspell_highlight_all(compose->gtkaspell);
10050 static void compose_allsel_cb(GtkAction *action, gpointer data)
10052 Compose *compose = (Compose *)data;
10053 if (compose->focused_editable
10054 #ifndef GENERIC_UMPC
10055 && gtkut_widget_has_focus(compose->focused_editable)
10058 entry_allsel(compose->focused_editable);
10061 static void textview_move_beginning_of_line (GtkTextView *text)
10063 GtkTextBuffer *buffer;
10067 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10069 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10070 mark = gtk_text_buffer_get_insert(buffer);
10071 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10072 gtk_text_iter_set_line_offset(&ins, 0);
10073 gtk_text_buffer_place_cursor(buffer, &ins);
10076 static void textview_move_forward_character (GtkTextView *text)
10078 GtkTextBuffer *buffer;
10082 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10084 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10085 mark = gtk_text_buffer_get_insert(buffer);
10086 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10087 if (gtk_text_iter_forward_cursor_position(&ins))
10088 gtk_text_buffer_place_cursor(buffer, &ins);
10091 static void textview_move_backward_character (GtkTextView *text)
10093 GtkTextBuffer *buffer;
10097 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10099 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10100 mark = gtk_text_buffer_get_insert(buffer);
10101 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10102 if (gtk_text_iter_backward_cursor_position(&ins))
10103 gtk_text_buffer_place_cursor(buffer, &ins);
10106 static void textview_move_forward_word (GtkTextView *text)
10108 GtkTextBuffer *buffer;
10113 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10115 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10116 mark = gtk_text_buffer_get_insert(buffer);
10117 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10118 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10119 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10120 gtk_text_iter_backward_word_start(&ins);
10121 gtk_text_buffer_place_cursor(buffer, &ins);
10125 static void textview_move_backward_word (GtkTextView *text)
10127 GtkTextBuffer *buffer;
10132 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10134 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10135 mark = gtk_text_buffer_get_insert(buffer);
10136 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10137 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10138 if (gtk_text_iter_backward_word_starts(&ins, 1))
10139 gtk_text_buffer_place_cursor(buffer, &ins);
10142 static void textview_move_end_of_line (GtkTextView *text)
10144 GtkTextBuffer *buffer;
10148 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10150 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10151 mark = gtk_text_buffer_get_insert(buffer);
10152 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10153 if (gtk_text_iter_forward_to_line_end(&ins))
10154 gtk_text_buffer_place_cursor(buffer, &ins);
10157 static void textview_move_next_line (GtkTextView *text)
10159 GtkTextBuffer *buffer;
10164 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10166 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10167 mark = gtk_text_buffer_get_insert(buffer);
10168 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10169 offset = gtk_text_iter_get_line_offset(&ins);
10170 if (gtk_text_iter_forward_line(&ins)) {
10171 gtk_text_iter_set_line_offset(&ins, offset);
10172 gtk_text_buffer_place_cursor(buffer, &ins);
10176 static void textview_move_previous_line (GtkTextView *text)
10178 GtkTextBuffer *buffer;
10183 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10185 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10186 mark = gtk_text_buffer_get_insert(buffer);
10187 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10188 offset = gtk_text_iter_get_line_offset(&ins);
10189 if (gtk_text_iter_backward_line(&ins)) {
10190 gtk_text_iter_set_line_offset(&ins, offset);
10191 gtk_text_buffer_place_cursor(buffer, &ins);
10195 static void textview_delete_forward_character (GtkTextView *text)
10197 GtkTextBuffer *buffer;
10199 GtkTextIter ins, end_iter;
10201 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10203 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10204 mark = gtk_text_buffer_get_insert(buffer);
10205 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10207 if (gtk_text_iter_forward_char(&end_iter)) {
10208 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10212 static void textview_delete_backward_character (GtkTextView *text)
10214 GtkTextBuffer *buffer;
10216 GtkTextIter ins, end_iter;
10218 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10220 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10221 mark = gtk_text_buffer_get_insert(buffer);
10222 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10224 if (gtk_text_iter_backward_char(&end_iter)) {
10225 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10229 static void textview_delete_forward_word (GtkTextView *text)
10231 GtkTextBuffer *buffer;
10233 GtkTextIter ins, end_iter;
10235 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10237 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10238 mark = gtk_text_buffer_get_insert(buffer);
10239 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10241 if (gtk_text_iter_forward_word_end(&end_iter)) {
10242 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10246 static void textview_delete_backward_word (GtkTextView *text)
10248 GtkTextBuffer *buffer;
10250 GtkTextIter ins, end_iter;
10252 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10254 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10255 mark = gtk_text_buffer_get_insert(buffer);
10256 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10258 if (gtk_text_iter_backward_word_start(&end_iter)) {
10259 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10263 static void textview_delete_line (GtkTextView *text)
10265 GtkTextBuffer *buffer;
10267 GtkTextIter ins, start_iter, end_iter;
10269 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10272 mark = gtk_text_buffer_get_insert(buffer);
10273 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10276 gtk_text_iter_set_line_offset(&start_iter, 0);
10279 if (gtk_text_iter_ends_line(&end_iter)){
10280 if (!gtk_text_iter_forward_char(&end_iter))
10281 gtk_text_iter_backward_char(&start_iter);
10284 gtk_text_iter_forward_to_line_end(&end_iter);
10285 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10288 static void textview_delete_to_line_end (GtkTextView *text)
10290 GtkTextBuffer *buffer;
10292 GtkTextIter ins, end_iter;
10294 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10296 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10297 mark = gtk_text_buffer_get_insert(buffer);
10298 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10300 if (gtk_text_iter_ends_line(&end_iter))
10301 gtk_text_iter_forward_char(&end_iter);
10303 gtk_text_iter_forward_to_line_end(&end_iter);
10304 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10307 #define DO_ACTION(name, act) { \
10308 if(!strcmp(name, a_name)) { \
10312 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10314 const gchar *a_name = gtk_action_get_name(action);
10315 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10316 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10317 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10318 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10319 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10320 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10321 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10322 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10323 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10324 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10325 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10326 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10327 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10328 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10332 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10334 Compose *compose = (Compose *)data;
10335 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10336 ComposeCallAdvancedAction action = -1;
10338 action = compose_call_advanced_action_from_path(gaction);
10341 void (*do_action) (GtkTextView *text);
10342 } action_table[] = {
10343 {textview_move_beginning_of_line},
10344 {textview_move_forward_character},
10345 {textview_move_backward_character},
10346 {textview_move_forward_word},
10347 {textview_move_backward_word},
10348 {textview_move_end_of_line},
10349 {textview_move_next_line},
10350 {textview_move_previous_line},
10351 {textview_delete_forward_character},
10352 {textview_delete_backward_character},
10353 {textview_delete_forward_word},
10354 {textview_delete_backward_word},
10355 {textview_delete_line},
10356 {textview_delete_to_line_end}
10359 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10361 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10362 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10363 if (action_table[action].do_action)
10364 action_table[action].do_action(text);
10366 g_warning("Not implemented yet.");
10370 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10374 if (GTK_IS_EDITABLE(widget)) {
10375 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10376 gtk_editable_set_position(GTK_EDITABLE(widget),
10379 if (widget->parent && widget->parent->parent
10380 && widget->parent->parent->parent) {
10381 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10382 gint y = widget->allocation.y;
10383 gint height = widget->allocation.height;
10384 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10385 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10387 if (y < (int)shown->value) {
10388 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10390 if (y + height > (int)shown->value + (int)shown->page_size) {
10391 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10392 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10393 y + height - (int)shown->page_size - 1);
10395 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10396 (int)shown->upper - (int)shown->page_size - 1);
10403 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10404 compose->focused_editable = widget;
10406 #ifdef GENERIC_UMPC
10407 if (GTK_IS_TEXT_VIEW(widget)
10408 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10409 g_object_ref(compose->notebook);
10410 g_object_ref(compose->edit_vbox);
10411 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10412 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10413 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10414 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10415 g_object_unref(compose->notebook);
10416 g_object_unref(compose->edit_vbox);
10417 g_signal_handlers_block_by_func(G_OBJECT(widget),
10418 G_CALLBACK(compose_grab_focus_cb),
10420 gtk_widget_grab_focus(widget);
10421 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10422 G_CALLBACK(compose_grab_focus_cb),
10424 } else if (!GTK_IS_TEXT_VIEW(widget)
10425 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10426 g_object_ref(compose->notebook);
10427 g_object_ref(compose->edit_vbox);
10428 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10429 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10430 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10431 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10432 g_object_unref(compose->notebook);
10433 g_object_unref(compose->edit_vbox);
10434 g_signal_handlers_block_by_func(G_OBJECT(widget),
10435 G_CALLBACK(compose_grab_focus_cb),
10437 gtk_widget_grab_focus(widget);
10438 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10439 G_CALLBACK(compose_grab_focus_cb),
10445 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10447 compose->modified = TRUE;
10448 // compose_beautify_paragraph(compose, NULL, TRUE);
10449 #ifndef GENERIC_UMPC
10450 compose_set_title(compose);
10454 static void compose_wrap_cb(GtkAction *action, gpointer data)
10456 Compose *compose = (Compose *)data;
10457 compose_beautify_paragraph(compose, NULL, TRUE);
10460 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10462 Compose *compose = (Compose *)data;
10463 compose_wrap_all_full(compose, TRUE);
10466 static void compose_find_cb(GtkAction *action, gpointer data)
10468 Compose *compose = (Compose *)data;
10470 message_search_compose(compose);
10473 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10476 Compose *compose = (Compose *)data;
10477 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10478 if (compose->autowrap)
10479 compose_wrap_all_full(compose, TRUE);
10480 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10483 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10486 Compose *compose = (Compose *)data;
10487 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10490 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10492 Compose *compose = (Compose *)data;
10494 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10497 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10499 Compose *compose = (Compose *)data;
10501 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10504 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10506 g_free(compose->privacy_system);
10508 compose->privacy_system = g_strdup(account->default_privacy_system);
10509 compose_update_privacy_system_menu_item(compose, warn);
10512 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10514 Compose *compose = (Compose *)data;
10516 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10517 gtk_widget_show(compose->ruler_hbox);
10518 prefs_common.show_ruler = TRUE;
10520 gtk_widget_hide(compose->ruler_hbox);
10521 gtk_widget_queue_resize(compose->edit_vbox);
10522 prefs_common.show_ruler = FALSE;
10526 static void compose_attach_drag_received_cb (GtkWidget *widget,
10527 GdkDragContext *context,
10530 GtkSelectionData *data,
10533 gpointer user_data)
10535 Compose *compose = (Compose *)user_data;
10538 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10540 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10542 ) && gtk_drag_get_source_widget(context) !=
10543 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10544 list = uri_list_extract_filenames((const gchar *)data->data);
10545 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10546 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10547 compose_attach_append
10548 (compose, (const gchar *)tmp->data,
10549 utf8_filename, NULL);
10550 g_free(utf8_filename);
10552 if (list) compose_changed_cb(NULL, compose);
10553 list_free_strings(list);
10555 } else if (gtk_drag_get_source_widget(context)
10556 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10557 /* comes from our summaryview */
10558 SummaryView * summaryview = NULL;
10559 GSList * list = NULL, *cur = NULL;
10561 if (mainwindow_get_mainwindow())
10562 summaryview = mainwindow_get_mainwindow()->summaryview;
10565 list = summary_get_selected_msg_list(summaryview);
10567 for (cur = list; cur; cur = cur->next) {
10568 MsgInfo *msginfo = (MsgInfo *)cur->data;
10569 gchar *file = NULL;
10571 file = procmsg_get_message_file_full(msginfo,
10574 compose_attach_append(compose, (const gchar *)file,
10575 (const gchar *)file, "message/rfc822");
10579 g_slist_free(list);
10583 static gboolean compose_drag_drop(GtkWidget *widget,
10584 GdkDragContext *drag_context,
10586 guint time, gpointer user_data)
10588 /* not handling this signal makes compose_insert_drag_received_cb
10593 static void compose_insert_drag_received_cb (GtkWidget *widget,
10594 GdkDragContext *drag_context,
10597 GtkSelectionData *data,
10600 gpointer user_data)
10602 Compose *compose = (Compose *)user_data;
10605 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10608 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10610 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10612 AlertValue val = G_ALERTDEFAULT;
10614 list = uri_list_extract_filenames((const gchar *)data->data);
10615 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10616 /* Assume a list of no files, and data has ://, is a remote link */
10617 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10618 gchar *tmpfile = get_tmp_file();
10619 str_write_to_file(tmpdata, tmpfile);
10621 compose_insert_file(compose, tmpfile);
10622 claws_unlink(tmpfile);
10624 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10625 compose_beautify_paragraph(compose, NULL, TRUE);
10628 switch (prefs_common.compose_dnd_mode) {
10629 case COMPOSE_DND_ASK:
10630 val = alertpanel_full(_("Insert or attach?"),
10631 _("Do you want to insert the contents of the file(s) "
10632 "into the message body, or attach it to the email?"),
10633 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10634 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10636 case COMPOSE_DND_INSERT:
10637 val = G_ALERTALTERNATE;
10639 case COMPOSE_DND_ATTACH:
10640 val = G_ALERTOTHER;
10643 /* unexpected case */
10644 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10647 if (val & G_ALERTDISABLE) {
10648 val &= ~G_ALERTDISABLE;
10649 /* remember what action to perform by default, only if we don't click Cancel */
10650 if (val == G_ALERTALTERNATE)
10651 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10652 else if (val == G_ALERTOTHER)
10653 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10656 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10657 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10658 list_free_strings(list);
10661 } else if (val == G_ALERTOTHER) {
10662 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10663 list_free_strings(list);
10668 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10669 compose_insert_file(compose, (const gchar *)tmp->data);
10671 list_free_strings(list);
10673 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10678 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10681 static void compose_header_drag_received_cb (GtkWidget *widget,
10682 GdkDragContext *drag_context,
10685 GtkSelectionData *data,
10688 gpointer user_data)
10690 GtkEditable *entry = (GtkEditable *)user_data;
10691 gchar *email = (gchar *)data->data;
10693 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10696 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10697 gchar *decoded=g_new(gchar, strlen(email));
10700 email += strlen("mailto:");
10701 decode_uri(decoded, email); /* will fit */
10702 gtk_editable_delete_text(entry, 0, -1);
10703 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10704 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10708 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10711 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10713 Compose *compose = (Compose *)data;
10715 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10716 compose->return_receipt = TRUE;
10718 compose->return_receipt = FALSE;
10721 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10723 Compose *compose = (Compose *)data;
10725 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10726 compose->remove_references = TRUE;
10728 compose->remove_references = FALSE;
10731 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10732 ComposeHeaderEntry *headerentry)
10734 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10738 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10739 GdkEventKey *event,
10740 ComposeHeaderEntry *headerentry)
10742 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10743 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10744 !(event->state & GDK_MODIFIER_MASK) &&
10745 (event->keyval == GDK_BackSpace) &&
10746 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10747 gtk_container_remove
10748 (GTK_CONTAINER(headerentry->compose->header_table),
10749 headerentry->combo);
10750 gtk_container_remove
10751 (GTK_CONTAINER(headerentry->compose->header_table),
10752 headerentry->entry);
10753 headerentry->compose->header_list =
10754 g_slist_remove(headerentry->compose->header_list,
10756 g_free(headerentry);
10757 } else if (event->keyval == GDK_Tab) {
10758 if (headerentry->compose->header_last == headerentry) {
10759 /* Override default next focus, and give it to subject_entry
10760 * instead of notebook tabs
10762 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10763 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10770 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10771 ComposeHeaderEntry *headerentry)
10773 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10774 compose_create_header_entry(headerentry->compose);
10775 g_signal_handlers_disconnect_matched
10776 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10777 0, 0, NULL, NULL, headerentry);
10779 /* Automatically scroll down */
10780 GTK_EVENTS_FLUSH();
10781 compose_show_first_last_header(headerentry->compose, FALSE);
10787 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10789 GtkAdjustment *vadj;
10791 cm_return_if_fail(compose);
10792 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10793 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10794 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10795 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10796 gtk_adjustment_changed(vadj);
10799 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10800 const gchar *text, gint len, Compose *compose)
10802 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10803 (G_OBJECT(compose->text), "paste_as_quotation"));
10806 cm_return_if_fail(text != NULL);
10808 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10809 G_CALLBACK(text_inserted),
10811 if (paste_as_quotation) {
10813 const gchar *qmark;
10815 GtkTextIter start_iter;
10818 len = strlen(text);
10820 new_text = g_strndup(text, len);
10822 qmark = compose_quote_char_from_context(compose);
10824 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10825 gtk_text_buffer_place_cursor(buffer, iter);
10827 pos = gtk_text_iter_get_offset(iter);
10829 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10830 _("Quote format error at line %d."));
10831 quote_fmt_reset_vartable();
10833 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10834 GINT_TO_POINTER(paste_as_quotation - 1));
10836 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10837 gtk_text_buffer_place_cursor(buffer, iter);
10838 gtk_text_buffer_delete_mark(buffer, mark);
10840 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10841 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10842 compose_beautify_paragraph(compose, &start_iter, FALSE);
10843 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10844 gtk_text_buffer_delete_mark(buffer, mark);
10846 if (strcmp(text, "\n") || compose->automatic_break
10847 || gtk_text_iter_starts_line(iter)) {
10848 GtkTextIter before_ins;
10849 gtk_text_buffer_insert(buffer, iter, text, len);
10850 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10851 before_ins = *iter;
10852 gtk_text_iter_backward_chars(&before_ins, len);
10853 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10856 /* check if the preceding is just whitespace or quote */
10857 GtkTextIter start_line;
10858 gchar *tmp = NULL, *quote = NULL;
10859 gint quote_len = 0, is_normal = 0;
10860 start_line = *iter;
10861 gtk_text_iter_set_line_offset(&start_line, 0);
10862 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10865 if (*tmp == '\0') {
10868 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10876 gtk_text_buffer_insert(buffer, iter, text, len);
10878 gtk_text_buffer_insert_with_tags_by_name(buffer,
10879 iter, text, len, "no_join", NULL);
10884 if (!paste_as_quotation) {
10885 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10886 compose_beautify_paragraph(compose, iter, FALSE);
10887 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10888 gtk_text_buffer_delete_mark(buffer, mark);
10891 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10892 G_CALLBACK(text_inserted),
10894 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10896 if (prefs_common.autosave &&
10897 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10898 compose->draft_timeout_tag != -2 /* disabled while loading */)
10899 compose->draft_timeout_tag = g_timeout_add
10900 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10902 static gint compose_defer_auto_save_draft(Compose *compose)
10904 compose->draft_timeout_tag = -1;
10905 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10910 static void compose_check_all(GtkAction *action, gpointer data)
10912 Compose *compose = (Compose *)data;
10913 if (!compose->gtkaspell)
10916 if (gtkut_widget_has_focus(compose->subject_entry))
10917 claws_spell_entry_check_all(
10918 CLAWS_SPELL_ENTRY(compose->subject_entry));
10920 gtkaspell_check_all(compose->gtkaspell);
10923 static void compose_highlight_all(GtkAction *action, gpointer data)
10925 Compose *compose = (Compose *)data;
10926 if (compose->gtkaspell) {
10927 claws_spell_entry_recheck_all(
10928 CLAWS_SPELL_ENTRY(compose->subject_entry));
10929 gtkaspell_highlight_all(compose->gtkaspell);
10933 static void compose_check_backwards(GtkAction *action, gpointer data)
10935 Compose *compose = (Compose *)data;
10936 if (!compose->gtkaspell) {
10937 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10941 if (gtkut_widget_has_focus(compose->subject_entry))
10942 claws_spell_entry_check_backwards(
10943 CLAWS_SPELL_ENTRY(compose->subject_entry));
10945 gtkaspell_check_backwards(compose->gtkaspell);
10948 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10950 Compose *compose = (Compose *)data;
10951 if (!compose->gtkaspell) {
10952 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10956 if (gtkut_widget_has_focus(compose->subject_entry))
10957 claws_spell_entry_check_forwards_go(
10958 CLAWS_SPELL_ENTRY(compose->subject_entry));
10960 gtkaspell_check_forwards_go(compose->gtkaspell);
10965 *\brief Guess originating forward account from MsgInfo and several
10966 * "common preference" settings. Return NULL if no guess.
10968 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10970 PrefsAccount *account = NULL;
10972 cm_return_val_if_fail(msginfo, NULL);
10973 cm_return_val_if_fail(msginfo->folder, NULL);
10974 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10976 if (msginfo->folder->prefs->enable_default_account)
10977 account = account_find_from_id(msginfo->folder->prefs->default_account);
10980 account = msginfo->folder->folder->account;
10982 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10984 Xstrdup_a(to, msginfo->to, return NULL);
10985 extract_address(to);
10986 account = account_find_from_address(to, FALSE);
10989 if (!account && prefs_common.forward_account_autosel) {
10990 gchar cc[BUFFSIZE];
10991 if (!procheader_get_header_from_msginfo
10992 (msginfo, cc,sizeof cc , "Cc:")) {
10993 gchar *buf = cc + strlen("Cc:");
10994 extract_address(buf);
10995 account = account_find_from_address(buf, FALSE);
10999 if (!account && prefs_common.forward_account_autosel) {
11000 gchar deliveredto[BUFFSIZE];
11001 if (!procheader_get_header_from_msginfo
11002 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11003 gchar *buf = deliveredto + strlen("Delivered-To:");
11004 extract_address(buf);
11005 account = account_find_from_address(buf, FALSE);
11012 gboolean compose_close(Compose *compose)
11016 if (!g_mutex_trylock(compose->mutex)) {
11017 /* we have to wait for the (possibly deferred by auto-save)
11018 * drafting to be done, before destroying the compose under
11020 debug_print("waiting for drafting to finish...\n");
11021 compose_allow_user_actions(compose, FALSE);
11022 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11025 cm_return_val_if_fail(compose, FALSE);
11026 gtkut_widget_get_uposition(compose->window, &x, &y);
11027 if (!compose->batch) {
11028 prefs_common.compose_x = x;
11029 prefs_common.compose_y = y;
11031 g_mutex_unlock(compose->mutex);
11032 compose_destroy(compose);
11037 * Add entry field for each address in list.
11038 * \param compose E-Mail composition object.
11039 * \param listAddress List of (formatted) E-Mail addresses.
11041 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11044 node = listAddress;
11046 addr = ( gchar * ) node->data;
11047 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11048 node = g_list_next( node );
11052 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11053 guint action, gboolean opening_multiple)
11055 gchar *body = NULL;
11056 GSList *new_msglist = NULL;
11057 MsgInfo *tmp_msginfo = NULL;
11058 gboolean originally_enc = FALSE;
11059 gboolean originally_sig = FALSE;
11060 Compose *compose = NULL;
11061 gchar *s_system = NULL;
11063 cm_return_if_fail(msgview != NULL);
11065 cm_return_if_fail(msginfo_list != NULL);
11067 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11068 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11069 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11071 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11072 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11073 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11074 orig_msginfo, mimeinfo);
11075 if (tmp_msginfo != NULL) {
11076 new_msglist = g_slist_append(NULL, tmp_msginfo);
11078 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11079 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11080 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11082 tmp_msginfo->folder = orig_msginfo->folder;
11083 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11084 if (orig_msginfo->tags) {
11085 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11086 tmp_msginfo->folder->tags_dirty = TRUE;
11092 if (!opening_multiple)
11093 body = messageview_get_selection(msgview);
11096 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11097 procmsg_msginfo_free(tmp_msginfo);
11098 g_slist_free(new_msglist);
11100 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11102 if (compose && originally_enc) {
11103 compose_force_encryption(compose, compose->account, FALSE, s_system);
11106 if (compose && originally_sig && compose->account->default_sign_reply) {
11107 compose_force_signing(compose, compose->account, s_system);
11111 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11114 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11117 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11118 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11119 GSList *cur = msginfo_list;
11120 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11121 "messages. Opening the windows "
11122 "could take some time. Do you "
11123 "want to continue?"),
11124 g_slist_length(msginfo_list));
11125 if (g_slist_length(msginfo_list) > 9
11126 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11127 != G_ALERTALTERNATE) {
11132 /* We'll open multiple compose windows */
11133 /* let the WM place the next windows */
11134 compose_force_window_origin = FALSE;
11135 for (; cur; cur = cur->next) {
11137 tmplist.data = cur->data;
11138 tmplist.next = NULL;
11139 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11141 compose_force_window_origin = TRUE;
11143 /* forwarding multiple mails as attachments is done via a
11144 * single compose window */
11145 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11149 void compose_check_for_email_account(Compose *compose)
11151 PrefsAccount *ac = NULL, *curr = NULL;
11157 if (compose->account && compose->account->protocol == A_NNTP) {
11158 ac = account_get_cur_account();
11159 if (ac->protocol == A_NNTP) {
11160 list = account_get_list();
11162 for( ; list != NULL ; list = g_list_next(list)) {
11163 curr = (PrefsAccount *) list->data;
11164 if (curr->protocol != A_NNTP) {
11170 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11175 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11176 const gchar *address)
11178 GSList *msginfo_list = NULL;
11179 gchar *body = messageview_get_selection(msgview);
11182 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11184 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11186 if (prefs_common.reply_account_autosel)
11187 compose_check_for_email_account(compose);
11189 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11190 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11191 compose_reply_set_subject(compose, msginfo);
11194 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11197 void compose_set_position(Compose *compose, gint pos)
11199 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11201 gtkut_text_view_set_position(text, pos);
11204 gboolean compose_search_string(Compose *compose,
11205 const gchar *str, gboolean case_sens)
11207 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11209 return gtkut_text_view_search_string(text, str, case_sens);
11212 gboolean compose_search_string_backward(Compose *compose,
11213 const gchar *str, gboolean case_sens)
11215 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11217 return gtkut_text_view_search_string_backward(text, str, case_sens);
11220 /* allocate a msginfo structure and populate its data from a compose data structure */
11221 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11223 MsgInfo *newmsginfo;
11225 gchar buf[BUFFSIZE];
11227 cm_return_val_if_fail( compose != NULL, NULL );
11229 newmsginfo = procmsg_msginfo_new();
11232 get_rfc822_date(buf, sizeof(buf));
11233 newmsginfo->date = g_strdup(buf);
11236 if (compose->from_name) {
11237 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11238 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11242 if (compose->subject_entry)
11243 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11245 /* to, cc, reply-to, newsgroups */
11246 for (list = compose->header_list; list; list = list->next) {
11247 gchar *header = gtk_editable_get_chars(
11249 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11250 gchar *entry = gtk_editable_get_chars(
11251 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11253 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11254 if ( newmsginfo->to == NULL ) {
11255 newmsginfo->to = g_strdup(entry);
11256 } else if (entry && *entry) {
11257 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11258 g_free(newmsginfo->to);
11259 newmsginfo->to = tmp;
11262 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11263 if ( newmsginfo->cc == NULL ) {
11264 newmsginfo->cc = g_strdup(entry);
11265 } else if (entry && *entry) {
11266 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11267 g_free(newmsginfo->cc);
11268 newmsginfo->cc = tmp;
11271 if ( strcasecmp(header,
11272 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11273 if ( newmsginfo->newsgroups == NULL ) {
11274 newmsginfo->newsgroups = g_strdup(entry);
11275 } else if (entry && *entry) {
11276 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11277 g_free(newmsginfo->newsgroups);
11278 newmsginfo->newsgroups = tmp;
11286 /* other data is unset */
11292 /* update compose's dictionaries from folder dict settings */
11293 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11294 FolderItem *folder_item)
11296 cm_return_if_fail(compose != NULL);
11298 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11299 FolderItemPrefs *prefs = folder_item->prefs;
11301 if (prefs->enable_default_dictionary)
11302 gtkaspell_change_dict(compose->gtkaspell,
11303 prefs->default_dictionary, FALSE);
11304 if (folder_item->prefs->enable_default_alt_dictionary)
11305 gtkaspell_change_alt_dict(compose->gtkaspell,
11306 prefs->default_alt_dictionary);
11307 if (prefs->enable_default_dictionary
11308 || prefs->enable_default_alt_dictionary)
11309 compose_spell_menu_changed(compose);