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 || (compose->followup_to &&
3198 !strncmp(compose->followup_to, "poster", 6)))
3199 compose_entry_append
3201 (compose->replyto ? compose->replyto :
3202 msginfo->from ? msginfo->from : ""),
3203 COMPOSE_TO, PREF_NONE);
3205 else if (followup_and_reply_to || to_all) {
3206 compose_entry_append
3208 (compose->replyto ? compose->replyto :
3209 msginfo->from ? msginfo->from : ""),
3210 COMPOSE_TO, PREF_NONE);
3212 compose_entry_append
3214 compose->followup_to ? compose->followup_to :
3215 compose->newsgroups ? compose->newsgroups : "",
3216 COMPOSE_NEWSGROUPS, PREF_NONE);
3217 } else if (reply_to_ml) {
3218 compose_check_for_email_account(compose);
3219 compose_entry_append(compose,
3220 compose->ml_post, COMPOSE_TO, PREF_ML);
3222 compose_entry_append
3224 compose->followup_to ? compose->followup_to :
3225 compose->newsgroups ? compose->newsgroups : "",
3226 COMPOSE_NEWSGROUPS, PREF_NONE);
3228 compose_reply_set_subject(compose, msginfo);
3230 if (to_ml && compose->ml_post) return;
3231 if (!to_all) return;
3233 if (compose->replyto) {
3234 Xstrdup_a(replyto, compose->replyto, return);
3235 extract_address(replyto);
3237 if (msginfo->from) {
3238 Xstrdup_a(from, msginfo->from, return);
3239 extract_address(from);
3242 if (replyto && from)
3243 cc_list = address_list_append_with_comments(cc_list, from);
3244 if (to_all && msginfo->folder &&
3245 msginfo->folder->prefs->enable_default_reply_to)
3246 cc_list = address_list_append_with_comments(cc_list,
3247 msginfo->folder->prefs->default_reply_to);
3248 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3249 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3251 ac_email = g_utf8_strdown(compose->account->address, -1);
3254 for (cur = cc_list; cur != NULL; cur = cur->next) {
3255 gchar *addr = g_utf8_strdown(cur->data, -1);
3256 extract_address(addr);
3258 if (strcmp(ac_email, addr))
3259 compose_entry_append(compose, (gchar *)cur->data,
3260 COMPOSE_CC, PREF_NONE);
3262 debug_print("Cc address same as compose account's, ignoring\n");
3267 slist_free_strings(cc_list);
3268 g_slist_free(cc_list);
3274 #define SET_ENTRY(entry, str) \
3277 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3280 #define SET_ADDRESS(type, str) \
3283 compose_entry_append(compose, str, type, PREF_NONE); \
3286 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3288 cm_return_if_fail(msginfo != NULL);
3290 SET_ENTRY(subject_entry, msginfo->subject);
3291 SET_ENTRY(from_name, msginfo->from);
3292 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3293 SET_ADDRESS(COMPOSE_CC, compose->cc);
3294 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3295 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3296 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3297 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3299 compose_update_priority_menu_item(compose);
3300 compose_update_privacy_system_menu_item(compose, FALSE);
3301 compose_show_first_last_header(compose, TRUE);
3307 static void compose_insert_sig(Compose *compose, gboolean replace)
3309 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3310 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3312 GtkTextIter iter, iter_end;
3313 gint cur_pos, ins_pos;
3314 gboolean prev_autowrap;
3315 gboolean found = FALSE;
3316 gboolean exists = FALSE;
3318 cm_return_if_fail(compose->account != NULL);
3322 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3323 G_CALLBACK(compose_changed_cb),
3326 mark = gtk_text_buffer_get_insert(buffer);
3327 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3328 cur_pos = gtk_text_iter_get_offset (&iter);
3331 gtk_text_buffer_get_end_iter(buffer, &iter);
3333 exists = (compose->sig_str != NULL);
3336 GtkTextIter first_iter, start_iter, end_iter;
3338 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3340 if (!exists || compose->sig_str[0] == '\0')
3343 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3344 compose->signature_tag);
3347 /* include previous \n\n */
3348 gtk_text_iter_backward_chars(&first_iter, 1);
3349 start_iter = first_iter;
3350 end_iter = first_iter;
3352 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3353 compose->signature_tag);
3354 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3355 compose->signature_tag);
3357 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3363 g_free(compose->sig_str);
3364 compose->sig_str = account_get_signature_str(compose->account);
3366 cur_pos = gtk_text_iter_get_offset(&iter);
3368 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3369 g_free(compose->sig_str);
3370 compose->sig_str = NULL;
3372 if (compose->sig_inserted == FALSE)
3373 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3374 compose->sig_inserted = TRUE;
3376 cur_pos = gtk_text_iter_get_offset(&iter);
3377 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3379 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3380 gtk_text_iter_forward_chars(&iter, 1);
3381 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3382 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3384 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3385 cur_pos = gtk_text_buffer_get_char_count (buffer);
3388 /* put the cursor where it should be
3389 * either where the quote_fmt says, either where it was */
3390 if (compose->set_cursor_pos < 0)
3391 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3393 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3394 compose->set_cursor_pos);
3396 compose->set_cursor_pos = -1;
3397 gtk_text_buffer_place_cursor(buffer, &iter);
3398 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3399 G_CALLBACK(compose_changed_cb),
3405 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3408 GtkTextBuffer *buffer;
3411 const gchar *cur_encoding;
3412 gchar buf[BUFFSIZE];
3415 gboolean prev_autowrap;
3416 gboolean badtxt = FALSE;
3417 struct stat file_stat;
3420 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3422 /* get the size of the file we are about to insert */
3423 ret = g_stat(file, &file_stat);
3425 gchar *shortfile = g_path_get_basename(file);
3426 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3428 return COMPOSE_INSERT_NO_FILE;
3429 } else if (prefs_common.warn_large_insert == TRUE) {
3431 /* ask user for confirmation if the file is large */
3432 if (prefs_common.warn_large_insert_size < 0 ||
3433 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3437 msg = g_strdup_printf(_("You are about to insert a file of %s "
3438 "in the message body. Are you sure you want to do that?"),
3439 to_human_readable(file_stat.st_size));
3440 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3441 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3444 /* do we ask for confirmation next time? */
3445 if (aval & G_ALERTDISABLE) {
3446 /* no confirmation next time, disable feature in preferences */
3447 aval &= ~G_ALERTDISABLE;
3448 prefs_common.warn_large_insert = FALSE;
3451 /* abort file insertion if user canceled action */
3452 if (aval != G_ALERTALTERNATE) {
3453 return COMPOSE_INSERT_NO_FILE;
3459 if ((fp = g_fopen(file, "rb")) == NULL) {
3460 FILE_OP_ERROR(file, "fopen");
3461 return COMPOSE_INSERT_READ_ERROR;
3464 prev_autowrap = compose->autowrap;
3465 compose->autowrap = FALSE;
3467 text = GTK_TEXT_VIEW(compose->text);
3468 buffer = gtk_text_view_get_buffer(text);
3469 mark = gtk_text_buffer_get_insert(buffer);
3470 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3472 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3473 G_CALLBACK(text_inserted),
3476 cur_encoding = conv_get_locale_charset_str_no_utf8();
3478 while (fgets(buf, sizeof(buf), fp) != NULL) {
3481 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3482 str = g_strdup(buf);
3484 str = conv_codeset_strdup
3485 (buf, cur_encoding, CS_INTERNAL);
3488 /* strip <CR> if DOS/Windows file,
3489 replace <CR> with <LF> if Macintosh file. */
3492 if (len > 0 && str[len - 1] != '\n') {
3494 if (str[len] == '\r') str[len] = '\n';
3497 gtk_text_buffer_insert(buffer, &iter, str, -1);
3501 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3502 G_CALLBACK(text_inserted),
3504 compose->autowrap = prev_autowrap;
3505 if (compose->autowrap)
3506 compose_wrap_all(compose);
3511 return COMPOSE_INSERT_INVALID_CHARACTER;
3513 return COMPOSE_INSERT_SUCCESS;
3516 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3517 const gchar *filename,
3518 const gchar *content_type)
3526 GtkListStore *store;
3528 gboolean has_binary = FALSE;
3530 if (!is_file_exist(file)) {
3531 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3532 gboolean result = FALSE;
3533 if (file_from_uri && is_file_exist(file_from_uri)) {
3534 result = compose_attach_append(
3535 compose, file_from_uri,
3539 g_free(file_from_uri);
3542 alertpanel_error("File %s doesn't exist\n", filename);
3545 if ((size = get_file_size(file)) < 0) {
3546 alertpanel_error("Can't get file size of %s\n", filename);
3550 alertpanel_error(_("File %s is empty."), filename);
3553 if ((fp = g_fopen(file, "rb")) == NULL) {
3554 alertpanel_error(_("Can't read %s."), filename);
3559 ainfo = g_new0(AttachInfo, 1);
3560 auto_ainfo = g_auto_pointer_new_with_free
3561 (ainfo, (GFreeFunc) compose_attach_info_free);
3562 ainfo->file = g_strdup(file);
3565 ainfo->content_type = g_strdup(content_type);
3566 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3568 MsgFlags flags = {0, 0};
3570 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3571 ainfo->encoding = ENC_7BIT;
3573 ainfo->encoding = ENC_8BIT;
3575 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3576 if (msginfo && msginfo->subject)
3577 name = g_strdup(msginfo->subject);
3579 name = g_path_get_basename(filename ? filename : file);
3581 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3583 procmsg_msginfo_free(msginfo);
3585 if (!g_ascii_strncasecmp(content_type, "text", 4))
3586 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3588 ainfo->encoding = ENC_BASE64;
3589 name = g_path_get_basename(filename ? filename : file);
3590 ainfo->name = g_strdup(name);
3594 ainfo->content_type = procmime_get_mime_type(file);
3595 if (!ainfo->content_type) {
3596 ainfo->content_type =
3597 g_strdup("application/octet-stream");
3598 ainfo->encoding = ENC_BASE64;
3599 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3601 procmime_get_encoding_for_text_file(file, &has_binary);
3603 ainfo->encoding = ENC_BASE64;
3604 name = g_path_get_basename(filename ? filename : file);
3605 ainfo->name = g_strdup(name);
3609 if (ainfo->name != NULL
3610 && !strcmp(ainfo->name, ".")) {
3611 g_free(ainfo->name);
3615 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3616 g_free(ainfo->content_type);
3617 ainfo->content_type = g_strdup("application/octet-stream");
3620 ainfo->size = (goffset)size;
3621 size_text = to_human_readable((goffset)size);
3623 store = GTK_LIST_STORE(gtk_tree_view_get_model
3624 (GTK_TREE_VIEW(compose->attach_clist)));
3626 gtk_list_store_append(store, &iter);
3627 gtk_list_store_set(store, &iter,
3628 COL_MIMETYPE, ainfo->content_type,
3629 COL_SIZE, size_text,
3630 COL_NAME, ainfo->name,
3632 COL_AUTODATA, auto_ainfo,
3635 g_auto_pointer_free(auto_ainfo);
3636 compose_attach_update_label(compose);
3640 static void compose_use_signing(Compose *compose, gboolean use_signing)
3642 compose->use_signing = use_signing;
3643 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3646 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3648 compose->use_encryption = use_encryption;
3649 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3652 #define NEXT_PART_NOT_CHILD(info) \
3654 node = info->node; \
3655 while (node->children) \
3656 node = g_node_last_child(node); \
3657 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3660 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3664 MimeInfo *firsttext = NULL;
3665 MimeInfo *encrypted = NULL;
3668 const gchar *partname = NULL;
3670 mimeinfo = procmime_scan_message(msginfo);
3671 if (!mimeinfo) return;
3673 if (mimeinfo->node->children == NULL) {
3674 procmime_mimeinfo_free_all(mimeinfo);
3678 /* find first content part */
3679 child = (MimeInfo *) mimeinfo->node->children->data;
3680 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3681 child = (MimeInfo *)child->node->children->data;
3684 if (child->type == MIMETYPE_TEXT) {
3686 debug_print("First text part found\n");
3687 } else if (compose->mode == COMPOSE_REEDIT &&
3688 child->type == MIMETYPE_APPLICATION &&
3689 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3690 encrypted = (MimeInfo *)child->node->parent->data;
3693 child = (MimeInfo *) mimeinfo->node->children->data;
3694 while (child != NULL) {
3697 if (child == encrypted) {
3698 /* skip this part of tree */
3699 NEXT_PART_NOT_CHILD(child);
3703 if (child->type == MIMETYPE_MULTIPART) {
3704 /* get the actual content */
3705 child = procmime_mimeinfo_next(child);
3709 if (child == firsttext) {
3710 child = procmime_mimeinfo_next(child);
3714 outfile = procmime_get_tmp_file_name(child);
3715 if ((err = procmime_get_part(outfile, child)) < 0)
3716 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3718 gchar *content_type;
3720 content_type = procmime_get_content_type_str(child->type, child->subtype);
3722 /* if we meet a pgp signature, we don't attach it, but
3723 * we force signing. */
3724 if ((strcmp(content_type, "application/pgp-signature") &&
3725 strcmp(content_type, "application/pkcs7-signature") &&
3726 strcmp(content_type, "application/x-pkcs7-signature"))
3727 || compose->mode == COMPOSE_REDIRECT) {
3728 partname = procmime_mimeinfo_get_parameter(child, "filename");
3729 if (partname == NULL)
3730 partname = procmime_mimeinfo_get_parameter(child, "name");
3731 if (partname == NULL)
3733 compose_attach_append(compose, outfile,
3734 partname, content_type);
3736 compose_force_signing(compose, compose->account, NULL);
3738 g_free(content_type);
3741 NEXT_PART_NOT_CHILD(child);
3743 procmime_mimeinfo_free_all(mimeinfo);
3746 #undef NEXT_PART_NOT_CHILD
3751 WAIT_FOR_INDENT_CHAR,
3752 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3755 /* return indent length, we allow:
3756 indent characters followed by indent characters or spaces/tabs,
3757 alphabets and numbers immediately followed by indent characters,
3758 and the repeating sequences of the above
3759 If quote ends with multiple spaces, only the first one is included. */
3760 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3761 const GtkTextIter *start, gint *len)
3763 GtkTextIter iter = *start;
3767 IndentState state = WAIT_FOR_INDENT_CHAR;
3770 gint alnum_count = 0;
3771 gint space_count = 0;
3774 if (prefs_common.quote_chars == NULL) {
3778 while (!gtk_text_iter_ends_line(&iter)) {
3779 wc = gtk_text_iter_get_char(&iter);
3780 if (g_unichar_iswide(wc))
3782 clen = g_unichar_to_utf8(wc, ch);
3786 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3787 is_space = g_unichar_isspace(wc);
3789 if (state == WAIT_FOR_INDENT_CHAR) {
3790 if (!is_indent && !g_unichar_isalnum(wc))
3793 quote_len += alnum_count + space_count + 1;
3794 alnum_count = space_count = 0;
3795 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3798 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3799 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3803 else if (is_indent) {
3804 quote_len += alnum_count + space_count + 1;
3805 alnum_count = space_count = 0;
3808 state = WAIT_FOR_INDENT_CHAR;
3812 gtk_text_iter_forward_char(&iter);
3815 if (quote_len > 0 && space_count > 0)
3821 if (quote_len > 0) {
3823 gtk_text_iter_forward_chars(&iter, quote_len);
3824 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3830 /* return >0 if the line is itemized */
3831 static int compose_itemized_length(GtkTextBuffer *buffer,
3832 const GtkTextIter *start)
3834 GtkTextIter iter = *start;
3839 if (gtk_text_iter_ends_line(&iter))
3844 wc = gtk_text_iter_get_char(&iter);
3845 if (!g_unichar_isspace(wc))
3847 gtk_text_iter_forward_char(&iter);
3848 if (gtk_text_iter_ends_line(&iter))
3852 clen = g_unichar_to_utf8(wc, ch);
3856 if (!strchr("*-+", ch[0]))
3859 gtk_text_iter_forward_char(&iter);
3860 if (gtk_text_iter_ends_line(&iter))
3862 wc = gtk_text_iter_get_char(&iter);
3863 if (g_unichar_isspace(wc)) {
3869 /* return the string at the start of the itemization */
3870 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3871 const GtkTextIter *start)
3873 GtkTextIter iter = *start;
3876 GString *item_chars = g_string_new("");
3879 if (gtk_text_iter_ends_line(&iter))
3884 wc = gtk_text_iter_get_char(&iter);
3885 if (!g_unichar_isspace(wc))
3887 gtk_text_iter_forward_char(&iter);
3888 if (gtk_text_iter_ends_line(&iter))
3890 g_string_append_unichar(item_chars, wc);
3893 str = item_chars->str;
3894 g_string_free(item_chars, FALSE);
3898 /* return the number of spaces at a line's start */
3899 static int compose_left_offset_length(GtkTextBuffer *buffer,
3900 const GtkTextIter *start)
3902 GtkTextIter iter = *start;
3905 if (gtk_text_iter_ends_line(&iter))
3909 wc = gtk_text_iter_get_char(&iter);
3910 if (!g_unichar_isspace(wc))
3913 gtk_text_iter_forward_char(&iter);
3914 if (gtk_text_iter_ends_line(&iter))
3918 gtk_text_iter_forward_char(&iter);
3919 if (gtk_text_iter_ends_line(&iter))
3924 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3925 const GtkTextIter *start,
3926 GtkTextIter *break_pos,
3930 GtkTextIter iter = *start, line_end = *start;
3931 PangoLogAttr *attrs;
3938 gboolean can_break = FALSE;
3939 gboolean do_break = FALSE;
3940 gboolean was_white = FALSE;
3941 gboolean prev_dont_break = FALSE;
3943 gtk_text_iter_forward_to_line_end(&line_end);
3944 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3945 len = g_utf8_strlen(str, -1);
3949 g_warning("compose_get_line_break_pos: len = 0!\n");
3953 /* g_print("breaking line: %d: %s (len = %d)\n",
3954 gtk_text_iter_get_line(&iter), str, len); */
3956 attrs = g_new(PangoLogAttr, len + 1);
3958 pango_default_break(str, -1, NULL, attrs, len + 1);
3962 /* skip quote and leading spaces */
3963 for (i = 0; *p != '\0' && i < len; i++) {
3966 wc = g_utf8_get_char(p);
3967 if (i >= quote_len && !g_unichar_isspace(wc))
3969 if (g_unichar_iswide(wc))
3971 else if (*p == '\t')
3975 p = g_utf8_next_char(p);
3978 for (; *p != '\0' && i < len; i++) {
3979 PangoLogAttr *attr = attrs + i;
3983 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3986 was_white = attr->is_white;
3988 /* don't wrap URI */
3989 if ((uri_len = get_uri_len(p)) > 0) {
3991 if (pos > 0 && col > max_col) {
4001 wc = g_utf8_get_char(p);
4002 if (g_unichar_iswide(wc)) {
4004 if (prev_dont_break && can_break && attr->is_line_break)
4006 } else if (*p == '\t')
4010 if (pos > 0 && col > max_col) {
4015 if (*p == '-' || *p == '/')
4016 prev_dont_break = TRUE;
4018 prev_dont_break = FALSE;
4020 p = g_utf8_next_char(p);
4024 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4029 *break_pos = *start;
4030 gtk_text_iter_set_line_offset(break_pos, pos);
4035 static gboolean compose_join_next_line(Compose *compose,
4036 GtkTextBuffer *buffer,
4038 const gchar *quote_str)
4040 GtkTextIter iter_ = *iter, cur, prev, next, end;
4041 PangoLogAttr attrs[3];
4043 gchar *next_quote_str;
4046 gboolean keep_cursor = FALSE;
4048 if (!gtk_text_iter_forward_line(&iter_) ||
4049 gtk_text_iter_ends_line(&iter_)) {
4052 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4054 if ((quote_str || next_quote_str) &&
4055 strcmp2(quote_str, next_quote_str) != 0) {
4056 g_free(next_quote_str);
4059 g_free(next_quote_str);
4062 if (quote_len > 0) {
4063 gtk_text_iter_forward_chars(&end, quote_len);
4064 if (gtk_text_iter_ends_line(&end)) {
4069 /* don't join itemized lines */
4070 if (compose_itemized_length(buffer, &end) > 0) {
4074 /* don't join signature separator */
4075 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4078 /* delete quote str */
4080 gtk_text_buffer_delete(buffer, &iter_, &end);
4082 /* don't join line breaks put by the user */
4084 gtk_text_iter_backward_char(&cur);
4085 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4086 gtk_text_iter_forward_char(&cur);
4090 gtk_text_iter_forward_char(&cur);
4091 /* delete linebreak and extra spaces */
4092 while (gtk_text_iter_backward_char(&cur)) {
4093 wc1 = gtk_text_iter_get_char(&cur);
4094 if (!g_unichar_isspace(wc1))
4099 while (!gtk_text_iter_ends_line(&cur)) {
4100 wc1 = gtk_text_iter_get_char(&cur);
4101 if (!g_unichar_isspace(wc1))
4103 gtk_text_iter_forward_char(&cur);
4106 if (!gtk_text_iter_equal(&prev, &next)) {
4109 mark = gtk_text_buffer_get_insert(buffer);
4110 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4111 if (gtk_text_iter_equal(&prev, &cur))
4113 gtk_text_buffer_delete(buffer, &prev, &next);
4117 /* insert space if required */
4118 gtk_text_iter_backward_char(&prev);
4119 wc1 = gtk_text_iter_get_char(&prev);
4120 wc2 = gtk_text_iter_get_char(&next);
4121 gtk_text_iter_forward_char(&next);
4122 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4123 pango_default_break(str, -1, NULL, attrs, 3);
4124 if (!attrs[1].is_line_break ||
4125 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4126 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4128 gtk_text_iter_backward_char(&iter_);
4129 gtk_text_buffer_place_cursor(buffer, &iter_);
4138 #define ADD_TXT_POS(bp_, ep_, pti_) \
4139 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4140 last = last->next; \
4141 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4142 last->next = NULL; \
4144 g_warning("alloc error scanning URIs\n"); \
4147 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4149 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4150 GtkTextBuffer *buffer;
4151 GtkTextIter iter, break_pos, end_of_line;
4152 gchar *quote_str = NULL;
4154 gboolean wrap_quote = prefs_common.linewrap_quote;
4155 gboolean prev_autowrap = compose->autowrap;
4156 gint startq_offset = -1, noq_offset = -1;
4157 gint uri_start = -1, uri_stop = -1;
4158 gint nouri_start = -1, nouri_stop = -1;
4159 gint num_blocks = 0;
4160 gint quotelevel = -1;
4161 gboolean modified = force;
4162 gboolean removed = FALSE;
4163 gboolean modified_before_remove = FALSE;
4165 gboolean start = TRUE;
4166 gint itemized_len = 0, rem_item_len = 0;
4167 gchar *itemized_chars = NULL;
4168 gboolean item_continuation = FALSE;
4173 if (compose->draft_timeout_tag == -2) {
4177 compose->autowrap = FALSE;
4179 buffer = gtk_text_view_get_buffer(text);
4180 undo_wrapping(compose->undostruct, TRUE);
4185 mark = gtk_text_buffer_get_insert(buffer);
4186 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4190 if (compose->draft_timeout_tag == -2) {
4191 if (gtk_text_iter_ends_line(&iter)) {
4192 while (gtk_text_iter_ends_line(&iter) &&
4193 gtk_text_iter_forward_line(&iter))
4196 while (gtk_text_iter_backward_line(&iter)) {
4197 if (gtk_text_iter_ends_line(&iter)) {
4198 gtk_text_iter_forward_line(&iter);
4204 /* move to line start */
4205 gtk_text_iter_set_line_offset(&iter, 0);
4208 itemized_len = compose_itemized_length(buffer, &iter);
4210 if (!itemized_len) {
4211 itemized_len = compose_left_offset_length(buffer, &iter);
4212 item_continuation = TRUE;
4216 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4218 /* go until paragraph end (empty line) */
4219 while (start || !gtk_text_iter_ends_line(&iter)) {
4220 gchar *scanpos = NULL;
4221 /* parse table - in order of priority */
4223 const gchar *needle; /* token */
4225 /* token search function */
4226 gchar *(*search) (const gchar *haystack,
4227 const gchar *needle);
4228 /* part parsing function */
4229 gboolean (*parse) (const gchar *start,
4230 const gchar *scanpos,
4234 /* part to URI function */
4235 gchar *(*build_uri) (const gchar *bp,
4239 static struct table parser[] = {
4240 {"http://", strcasestr, get_uri_part, make_uri_string},
4241 {"https://", strcasestr, get_uri_part, make_uri_string},
4242 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4243 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4244 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4245 {"www.", strcasestr, get_uri_part, make_http_string},
4246 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4247 {"@", strcasestr, get_email_part, make_email_string}
4249 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4250 gint last_index = PARSE_ELEMS;
4252 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4256 if (!prev_autowrap && num_blocks == 0) {
4258 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4259 G_CALLBACK(text_inserted),
4262 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4265 uri_start = uri_stop = -1;
4267 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4270 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4271 if (startq_offset == -1)
4272 startq_offset = gtk_text_iter_get_offset(&iter);
4273 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4274 if (quotelevel > 2) {
4275 /* recycle colors */
4276 if (prefs_common.recycle_quote_colors)
4285 if (startq_offset == -1)
4286 noq_offset = gtk_text_iter_get_offset(&iter);
4290 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4293 if (gtk_text_iter_ends_line(&iter)) {
4295 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4296 prefs_common.linewrap_len,
4298 GtkTextIter prev, next, cur;
4299 if (prev_autowrap != FALSE || force) {
4300 compose->automatic_break = TRUE;
4302 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4303 compose->automatic_break = FALSE;
4304 if (itemized_len && compose->autoindent) {
4305 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4306 if (!item_continuation)
4307 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4309 } else if (quote_str && wrap_quote) {
4310 compose->automatic_break = TRUE;
4312 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4313 compose->automatic_break = FALSE;
4314 if (itemized_len && compose->autoindent) {
4315 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4316 if (!item_continuation)
4317 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4321 /* remove trailing spaces */
4323 rem_item_len = itemized_len;
4324 while (compose->autoindent && rem_item_len-- > 0)
4325 gtk_text_iter_backward_char(&cur);
4326 gtk_text_iter_backward_char(&cur);
4329 while (!gtk_text_iter_starts_line(&cur)) {
4332 gtk_text_iter_backward_char(&cur);
4333 wc = gtk_text_iter_get_char(&cur);
4334 if (!g_unichar_isspace(wc))
4338 if (!gtk_text_iter_equal(&prev, &next)) {
4339 gtk_text_buffer_delete(buffer, &prev, &next);
4341 gtk_text_iter_forward_char(&break_pos);
4345 gtk_text_buffer_insert(buffer, &break_pos,
4349 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4351 /* move iter to current line start */
4352 gtk_text_iter_set_line_offset(&iter, 0);
4359 /* move iter to next line start */
4365 if (!prev_autowrap && num_blocks > 0) {
4367 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4368 G_CALLBACK(text_inserted),
4372 while (!gtk_text_iter_ends_line(&end_of_line)) {
4373 gtk_text_iter_forward_char(&end_of_line);
4375 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4377 nouri_start = gtk_text_iter_get_offset(&iter);
4378 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4380 walk_pos = gtk_text_iter_get_offset(&iter);
4381 /* FIXME: this looks phony. scanning for anything in the parse table */
4382 for (n = 0; n < PARSE_ELEMS; n++) {
4385 tmp = parser[n].search(walk, parser[n].needle);
4387 if (scanpos == NULL || tmp < scanpos) {
4396 /* check if URI can be parsed */
4397 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4398 (const gchar **)&ep, FALSE)
4399 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4403 strlen(parser[last_index].needle);
4406 uri_start = walk_pos + (bp - o_walk);
4407 uri_stop = walk_pos + (ep - o_walk);
4411 gtk_text_iter_forward_line(&iter);
4414 if (startq_offset != -1) {
4415 GtkTextIter startquote, endquote;
4416 gtk_text_buffer_get_iter_at_offset(
4417 buffer, &startquote, startq_offset);
4420 switch (quotelevel) {
4422 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4423 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4424 gtk_text_buffer_apply_tag_by_name(
4425 buffer, "quote0", &startquote, &endquote);
4426 gtk_text_buffer_remove_tag_by_name(
4427 buffer, "quote1", &startquote, &endquote);
4428 gtk_text_buffer_remove_tag_by_name(
4429 buffer, "quote2", &startquote, &endquote);
4434 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4435 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4436 gtk_text_buffer_apply_tag_by_name(
4437 buffer, "quote1", &startquote, &endquote);
4438 gtk_text_buffer_remove_tag_by_name(
4439 buffer, "quote0", &startquote, &endquote);
4440 gtk_text_buffer_remove_tag_by_name(
4441 buffer, "quote2", &startquote, &endquote);
4446 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4447 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4448 gtk_text_buffer_apply_tag_by_name(
4449 buffer, "quote2", &startquote, &endquote);
4450 gtk_text_buffer_remove_tag_by_name(
4451 buffer, "quote0", &startquote, &endquote);
4452 gtk_text_buffer_remove_tag_by_name(
4453 buffer, "quote1", &startquote, &endquote);
4459 } else if (noq_offset != -1) {
4460 GtkTextIter startnoquote, endnoquote;
4461 gtk_text_buffer_get_iter_at_offset(
4462 buffer, &startnoquote, noq_offset);
4465 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4466 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4467 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4468 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4469 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4470 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4471 gtk_text_buffer_remove_tag_by_name(
4472 buffer, "quote0", &startnoquote, &endnoquote);
4473 gtk_text_buffer_remove_tag_by_name(
4474 buffer, "quote1", &startnoquote, &endnoquote);
4475 gtk_text_buffer_remove_tag_by_name(
4476 buffer, "quote2", &startnoquote, &endnoquote);
4482 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4483 GtkTextIter nouri_start_iter, nouri_end_iter;
4484 gtk_text_buffer_get_iter_at_offset(
4485 buffer, &nouri_start_iter, nouri_start);
4486 gtk_text_buffer_get_iter_at_offset(
4487 buffer, &nouri_end_iter, nouri_stop);
4488 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4489 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4490 gtk_text_buffer_remove_tag_by_name(
4491 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4492 modified_before_remove = modified;
4497 if (uri_start >= 0 && uri_stop > 0) {
4498 GtkTextIter uri_start_iter, uri_end_iter, back;
4499 gtk_text_buffer_get_iter_at_offset(
4500 buffer, &uri_start_iter, uri_start);
4501 gtk_text_buffer_get_iter_at_offset(
4502 buffer, &uri_end_iter, uri_stop);
4503 back = uri_end_iter;
4504 gtk_text_iter_backward_char(&back);
4505 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4506 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4507 gtk_text_buffer_apply_tag_by_name(
4508 buffer, "link", &uri_start_iter, &uri_end_iter);
4510 if (removed && !modified_before_remove) {
4516 // debug_print("not modified, out after %d lines\n", lines);
4520 // debug_print("modified, out after %d lines\n", lines);
4522 g_free(itemized_chars);
4525 undo_wrapping(compose->undostruct, FALSE);
4526 compose->autowrap = prev_autowrap;
4531 void compose_action_cb(void *data)
4533 Compose *compose = (Compose *)data;
4534 compose_wrap_all(compose);
4537 static void compose_wrap_all(Compose *compose)
4539 compose_wrap_all_full(compose, FALSE);
4542 static void compose_wrap_all_full(Compose *compose, gboolean force)
4544 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4545 GtkTextBuffer *buffer;
4547 gboolean modified = TRUE;
4549 buffer = gtk_text_view_get_buffer(text);
4551 gtk_text_buffer_get_start_iter(buffer, &iter);
4552 while (!gtk_text_iter_is_end(&iter) && modified)
4553 modified = compose_beautify_paragraph(compose, &iter, force);
4557 static void compose_set_title(Compose *compose)
4563 edited = compose->modified ? _(" [Edited]") : "";
4565 subject = gtk_editable_get_chars(
4566 GTK_EDITABLE(compose->subject_entry), 0, -1);
4568 #ifndef GENERIC_UMPC
4569 if (subject && strlen(subject))
4570 str = g_strdup_printf(_("%s - Compose message%s"),
4573 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4575 str = g_strdup(_("Compose message"));
4578 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4584 * compose_current_mail_account:
4586 * Find a current mail account (the currently selected account, or the
4587 * default account, if a news account is currently selected). If a
4588 * mail account cannot be found, display an error message.
4590 * Return value: Mail account, or NULL if not found.
4592 static PrefsAccount *
4593 compose_current_mail_account(void)
4597 if (cur_account && cur_account->protocol != A_NNTP)
4600 ac = account_get_default();
4601 if (!ac || ac->protocol == A_NNTP) {
4602 alertpanel_error(_("Account for sending mail is not specified.\n"
4603 "Please select a mail account before sending."));
4610 #define QUOTE_IF_REQUIRED(out, str) \
4612 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4616 len = strlen(str) + 3; \
4617 if ((__tmp = alloca(len)) == NULL) { \
4618 g_warning("can't allocate memory\n"); \
4619 g_string_free(header, TRUE); \
4622 g_snprintf(__tmp, len, "\"%s\"", str); \
4627 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4628 g_warning("can't allocate memory\n"); \
4629 g_string_free(header, TRUE); \
4632 strcpy(__tmp, str); \
4638 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4640 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4644 len = strlen(str) + 3; \
4645 if ((__tmp = alloca(len)) == NULL) { \
4646 g_warning("can't allocate memory\n"); \
4649 g_snprintf(__tmp, len, "\"%s\"", str); \
4654 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4655 g_warning("can't allocate memory\n"); \
4658 strcpy(__tmp, str); \
4664 static void compose_select_account(Compose *compose, PrefsAccount *account,
4667 gchar *from = NULL, *header;
4668 ComposeHeaderEntry *header_entry;
4670 cm_return_if_fail(account != NULL);
4672 compose->account = account;
4673 if (account->name && *account->name) {
4675 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4676 from = g_strdup_printf("%s <%s>",
4677 buf, account->address);
4678 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4680 from = g_strdup_printf("<%s>",
4682 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4687 compose_set_title(compose);
4689 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4690 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4692 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4693 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4694 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4696 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4698 activate_privacy_system(compose, account, FALSE);
4700 if (!init && compose->mode != COMPOSE_REDIRECT) {
4701 undo_block(compose->undostruct);
4702 compose_insert_sig(compose, TRUE);
4703 undo_unblock(compose->undostruct);
4706 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4707 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4709 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4710 if (account->protocol == A_NNTP) {
4711 if (!strcmp(header, _("To:")))
4712 combobox_select_by_text(
4713 GTK_COMBO_BOX(header_entry->combo),
4716 if (!strcmp(header, _("Newsgroups:")))
4717 combobox_select_by_text(
4718 GTK_COMBO_BOX(header_entry->combo),
4726 /* use account's dict info if set */
4727 if (compose->gtkaspell) {
4728 if (account->enable_default_dictionary)
4729 gtkaspell_change_dict(compose->gtkaspell,
4730 account->default_dictionary, FALSE);
4731 if (account->enable_default_alt_dictionary)
4732 gtkaspell_change_alt_dict(compose->gtkaspell,
4733 account->default_alt_dictionary);
4734 if (account->enable_default_dictionary
4735 || account->enable_default_alt_dictionary)
4736 compose_spell_menu_changed(compose);
4741 gboolean compose_check_for_valid_recipient(Compose *compose) {
4742 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4743 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4744 gboolean recipient_found = FALSE;
4748 /* free to and newsgroup list */
4749 slist_free_strings(compose->to_list);
4750 g_slist_free(compose->to_list);
4751 compose->to_list = NULL;
4753 slist_free_strings(compose->newsgroup_list);
4754 g_slist_free(compose->newsgroup_list);
4755 compose->newsgroup_list = NULL;
4757 /* search header entries for to and newsgroup entries */
4758 for (list = compose->header_list; list; list = list->next) {
4761 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4762 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4765 if (entry[0] != '\0') {
4766 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4767 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4768 compose->to_list = address_list_append(compose->to_list, entry);
4769 recipient_found = TRUE;
4772 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4773 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4774 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4775 recipient_found = TRUE;
4782 return recipient_found;
4785 static gboolean compose_check_for_set_recipients(Compose *compose)
4787 if (compose->account->set_autocc && compose->account->auto_cc) {
4788 gboolean found_other = FALSE;
4790 /* search header entries for to and newsgroup entries */
4791 for (list = compose->header_list; list; list = list->next) {
4794 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4795 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4798 if (strcmp(entry, compose->account->auto_cc)
4799 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4809 if (compose->batch) {
4810 gtk_widget_show_all(compose->window);
4812 aval = alertpanel(_("Send"),
4813 _("The only recipient is the default CC address. Send anyway?"),
4814 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4815 if (aval != G_ALERTALTERNATE)
4819 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4820 gboolean found_other = FALSE;
4822 /* search header entries for to and newsgroup entries */
4823 for (list = compose->header_list; list; list = list->next) {
4826 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4827 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4830 if (strcmp(entry, compose->account->auto_bcc)
4831 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4841 if (compose->batch) {
4842 gtk_widget_show_all(compose->window);
4844 aval = alertpanel(_("Send"),
4845 _("The only recipient is the default BCC address. Send anyway?"),
4846 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4847 if (aval != G_ALERTALTERNATE)
4854 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4858 if (compose_check_for_valid_recipient(compose) == FALSE) {
4859 if (compose->batch) {
4860 gtk_widget_show_all(compose->window);
4862 alertpanel_error(_("Recipient is not specified."));
4866 if (compose_check_for_set_recipients(compose) == FALSE) {
4870 if (!compose->batch) {
4871 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4872 if (*str == '\0' && check_everything == TRUE &&
4873 compose->mode != COMPOSE_REDIRECT) {
4875 gchar *button_label;
4878 if (compose->sending)
4879 button_label = _("+_Send");
4881 button_label = _("+_Queue");
4882 message = g_strdup_printf(_("Subject is empty. %s"),
4883 compose->sending?_("Send it anyway?"):
4884 _("Queue it anyway?"));
4886 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4887 GTK_STOCK_CANCEL, button_label, NULL);
4889 if (aval != G_ALERTALTERNATE)
4894 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4900 gint compose_send(Compose *compose)
4903 FolderItem *folder = NULL;
4905 gchar *msgpath = NULL;
4906 gboolean discard_window = FALSE;
4907 gchar *errstr = NULL;
4908 gchar *tmsgid = NULL;
4909 MainWindow *mainwin = mainwindow_get_mainwindow();
4910 gboolean queued_removed = FALSE;
4912 if (prefs_common.send_dialog_invisible
4913 || compose->batch == TRUE)
4914 discard_window = TRUE;
4916 compose_allow_user_actions (compose, FALSE);
4917 compose->sending = TRUE;
4919 if (compose_check_entries(compose, TRUE) == FALSE) {
4920 if (compose->batch) {
4921 gtk_widget_show_all(compose->window);
4927 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4930 if (compose->batch) {
4931 gtk_widget_show_all(compose->window);
4934 alertpanel_error(_("Could not queue message for sending:\n\n"
4935 "Charset conversion failed."));
4936 } else if (val == -5) {
4937 alertpanel_error(_("Could not queue message for sending:\n\n"
4938 "Couldn't get recipient encryption key."));
4939 } else if (val == -6) {
4941 } else if (val == -3) {
4942 if (privacy_peek_error())
4943 alertpanel_error(_("Could not queue message for sending:\n\n"
4944 "Signature failed: %s"), privacy_get_error());
4945 } else if (val == -2 && errno != 0) {
4946 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4948 alertpanel_error(_("Could not queue message for sending."));
4953 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4954 if (discard_window) {
4955 compose->sending = FALSE;
4956 compose_close(compose);
4957 /* No more compose access in the normal codepath
4958 * after this point! */
4963 alertpanel_error(_("The message was queued but could not be "
4964 "sent.\nUse \"Send queued messages\" from "
4965 "the main window to retry."));
4966 if (!discard_window) {
4973 if (msgpath == NULL) {
4974 msgpath = folder_item_fetch_msg(folder, msgnum);
4975 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4978 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4979 claws_unlink(msgpath);
4982 if (!discard_window) {
4984 if (!queued_removed)
4985 folder_item_remove_msg(folder, msgnum);
4986 folder_item_scan(folder);
4988 /* make sure we delete that */
4989 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4991 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4992 folder_item_remove_msg(folder, tmp->msgnum);
4993 procmsg_msginfo_free(tmp);
5000 if (!queued_removed)
5001 folder_item_remove_msg(folder, msgnum);
5002 folder_item_scan(folder);
5004 /* make sure we delete that */
5005 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5007 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5008 folder_item_remove_msg(folder, tmp->msgnum);
5009 procmsg_msginfo_free(tmp);
5012 if (!discard_window) {
5013 compose->sending = FALSE;
5014 compose_allow_user_actions (compose, TRUE);
5015 compose_close(compose);
5019 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5020 "the main window to retry."), errstr);
5023 alertpanel_error_log(_("The message was queued but could not be "
5024 "sent.\nUse \"Send queued messages\" from "
5025 "the main window to retry."));
5027 if (!discard_window) {
5036 toolbar_main_set_sensitive(mainwin);
5037 main_window_set_menu_sensitive(mainwin);
5043 compose_allow_user_actions (compose, TRUE);
5044 compose->sending = FALSE;
5045 compose->modified = TRUE;
5046 toolbar_main_set_sensitive(mainwin);
5047 main_window_set_menu_sensitive(mainwin);
5052 static gboolean compose_use_attach(Compose *compose)
5054 GtkTreeModel *model = gtk_tree_view_get_model
5055 (GTK_TREE_VIEW(compose->attach_clist));
5056 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5059 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5062 gchar buf[BUFFSIZE];
5064 gboolean first_to_address;
5065 gboolean first_cc_address;
5067 ComposeHeaderEntry *headerentry;
5068 const gchar *headerentryname;
5069 const gchar *cc_hdr;
5070 const gchar *to_hdr;
5071 gboolean err = FALSE;
5073 debug_print("Writing redirect header\n");
5075 cc_hdr = prefs_common_translated_header_name("Cc:");
5076 to_hdr = prefs_common_translated_header_name("To:");
5078 first_to_address = TRUE;
5079 for (list = compose->header_list; list; list = list->next) {
5080 headerentry = ((ComposeHeaderEntry *)list->data);
5081 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5083 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5084 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5085 Xstrdup_a(str, entstr, return -1);
5087 if (str[0] != '\0') {
5088 compose_convert_header
5089 (compose, buf, sizeof(buf), str,
5090 strlen("Resent-To") + 2, TRUE);
5092 if (first_to_address) {
5093 err |= (fprintf(fp, "Resent-To: ") < 0);
5094 first_to_address = FALSE;
5096 err |= (fprintf(fp, ",") < 0);
5098 err |= (fprintf(fp, "%s", buf) < 0);
5102 if (!first_to_address) {
5103 err |= (fprintf(fp, "\n") < 0);
5106 first_cc_address = TRUE;
5107 for (list = compose->header_list; list; list = list->next) {
5108 headerentry = ((ComposeHeaderEntry *)list->data);
5109 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5111 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5112 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5113 Xstrdup_a(str, strg, return -1);
5115 if (str[0] != '\0') {
5116 compose_convert_header
5117 (compose, buf, sizeof(buf), str,
5118 strlen("Resent-Cc") + 2, TRUE);
5120 if (first_cc_address) {
5121 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5122 first_cc_address = FALSE;
5124 err |= (fprintf(fp, ",") < 0);
5126 err |= (fprintf(fp, "%s", buf) < 0);
5130 if (!first_cc_address) {
5131 err |= (fprintf(fp, "\n") < 0);
5134 return (err ? -1:0);
5137 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5139 gchar buf[BUFFSIZE];
5141 const gchar *entstr;
5142 /* struct utsname utsbuf; */
5143 gboolean err = FALSE;
5145 cm_return_val_if_fail(fp != NULL, -1);
5146 cm_return_val_if_fail(compose->account != NULL, -1);
5147 cm_return_val_if_fail(compose->account->address != NULL, -1);
5150 get_rfc822_date(buf, sizeof(buf));
5151 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5154 if (compose->account->name && *compose->account->name) {
5155 compose_convert_header
5156 (compose, buf, sizeof(buf), compose->account->name,
5157 strlen("From: "), TRUE);
5158 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5159 buf, compose->account->address) < 0);
5161 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5164 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5165 if (*entstr != '\0') {
5166 Xstrdup_a(str, entstr, return -1);
5169 compose_convert_header(compose, buf, sizeof(buf), str,
5170 strlen("Subject: "), FALSE);
5171 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5175 /* Resent-Message-ID */
5176 if (compose->account->set_domain && compose->account->domain) {
5177 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5178 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5179 g_snprintf(buf, sizeof(buf), "%s",
5180 strchr(compose->account->address, '@') ?
5181 strchr(compose->account->address, '@')+1 :
5182 compose->account->address);
5184 g_snprintf(buf, sizeof(buf), "%s", "");
5187 if (compose->account->gen_msgid) {
5189 if (compose->account->msgid_with_addr) {
5190 addr = compose->account->address;
5192 generate_msgid(buf, sizeof(buf), addr);
5193 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5194 compose->msgid = g_strdup(buf);
5196 compose->msgid = NULL;
5199 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5202 /* separator between header and body */
5203 err |= (fputs("\n", fp) == EOF);
5205 return (err ? -1:0);
5208 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5212 gchar buf[BUFFSIZE];
5214 gboolean skip = FALSE;
5215 gboolean err = FALSE;
5216 gchar *not_included[]={
5217 "Return-Path:", "Delivered-To:", "Received:",
5218 "Subject:", "X-UIDL:", "AF:",
5219 "NF:", "PS:", "SRH:",
5220 "SFN:", "DSR:", "MID:",
5221 "CFG:", "PT:", "S:",
5222 "RQ:", "SSV:", "NSV:",
5223 "SSH:", "R:", "MAID:",
5224 "NAID:", "RMID:", "FMID:",
5225 "SCF:", "RRCPT:", "NG:",
5226 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5227 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5228 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5229 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5230 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5233 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5234 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5238 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5240 for (i = 0; not_included[i] != NULL; i++) {
5241 if (g_ascii_strncasecmp(buf, not_included[i],
5242 strlen(not_included[i])) == 0) {
5249 if (fputs(buf, fdest) == -1)
5252 if (!prefs_common.redirect_keep_from) {
5253 if (g_ascii_strncasecmp(buf, "From:",
5254 strlen("From:")) == 0) {
5255 err |= (fputs(" (by way of ", fdest) == EOF);
5256 if (compose->account->name
5257 && *compose->account->name) {
5258 compose_convert_header
5259 (compose, buf, sizeof(buf),
5260 compose->account->name,
5263 err |= (fprintf(fdest, "%s <%s>",
5265 compose->account->address) < 0);
5267 err |= (fprintf(fdest, "%s",
5268 compose->account->address) < 0);
5269 err |= (fputs(")", fdest) == EOF);
5273 if (fputs("\n", fdest) == -1)
5280 if (compose_redirect_write_headers(compose, fdest))
5283 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5284 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5297 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5299 GtkTextBuffer *buffer;
5300 GtkTextIter start, end;
5303 const gchar *out_codeset;
5304 EncodingType encoding = ENC_UNKNOWN;
5305 MimeInfo *mimemsg, *mimetext;
5307 const gchar *src_codeset = CS_INTERNAL;
5308 gchar *from_addr = NULL;
5309 gchar *from_name = NULL;
5311 if (action == COMPOSE_WRITE_FOR_SEND)
5312 attach_parts = TRUE;
5314 /* create message MimeInfo */
5315 mimemsg = procmime_mimeinfo_new();
5316 mimemsg->type = MIMETYPE_MESSAGE;
5317 mimemsg->subtype = g_strdup("rfc822");
5318 mimemsg->content = MIMECONTENT_MEM;
5319 mimemsg->tmp = TRUE; /* must free content later */
5320 mimemsg->data.mem = compose_get_header(compose);
5322 /* Create text part MimeInfo */
5323 /* get all composed text */
5324 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5325 gtk_text_buffer_get_start_iter(buffer, &start);
5326 gtk_text_buffer_get_end_iter(buffer, &end);
5327 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5329 out_codeset = conv_get_charset_str(compose->out_encoding);
5331 if (!out_codeset && is_ascii_str(chars)) {
5332 out_codeset = CS_US_ASCII;
5333 } else if (prefs_common.outgoing_fallback_to_ascii &&
5334 is_ascii_str(chars)) {
5335 out_codeset = CS_US_ASCII;
5336 encoding = ENC_7BIT;
5340 gchar *test_conv_global_out = NULL;
5341 gchar *test_conv_reply = NULL;
5343 /* automatic mode. be automatic. */
5344 codeconv_set_strict(TRUE);
5346 out_codeset = conv_get_outgoing_charset_str();
5348 debug_print("trying to convert to %s\n", out_codeset);
5349 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5352 if (!test_conv_global_out && compose->orig_charset
5353 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5354 out_codeset = compose->orig_charset;
5355 debug_print("failure; trying to convert to %s\n", out_codeset);
5356 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5359 if (!test_conv_global_out && !test_conv_reply) {
5361 out_codeset = CS_INTERNAL;
5362 debug_print("failure; finally using %s\n", out_codeset);
5364 g_free(test_conv_global_out);
5365 g_free(test_conv_reply);
5366 codeconv_set_strict(FALSE);
5369 if (encoding == ENC_UNKNOWN) {
5370 if (prefs_common.encoding_method == CTE_BASE64)
5371 encoding = ENC_BASE64;
5372 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5373 encoding = ENC_QUOTED_PRINTABLE;
5374 else if (prefs_common.encoding_method == CTE_8BIT)
5375 encoding = ENC_8BIT;
5377 encoding = procmime_get_encoding_for_charset(out_codeset);
5380 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5381 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5383 if (action == COMPOSE_WRITE_FOR_SEND) {
5384 codeconv_set_strict(TRUE);
5385 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5386 codeconv_set_strict(FALSE);
5392 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5393 "to the specified %s charset.\n"
5394 "Send it as %s?"), out_codeset, src_codeset);
5395 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5396 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5399 if (aval != G_ALERTALTERNATE) {
5404 out_codeset = src_codeset;
5410 out_codeset = src_codeset;
5415 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5416 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5417 strstr(buf, "\nFrom ") != NULL) {
5418 encoding = ENC_QUOTED_PRINTABLE;
5422 mimetext = procmime_mimeinfo_new();
5423 mimetext->content = MIMECONTENT_MEM;
5424 mimetext->tmp = TRUE; /* must free content later */
5425 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5426 * and free the data, which we need later. */
5427 mimetext->data.mem = g_strdup(buf);
5428 mimetext->type = MIMETYPE_TEXT;
5429 mimetext->subtype = g_strdup("plain");
5430 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5431 g_strdup(out_codeset));
5433 /* protect trailing spaces when signing message */
5434 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5435 privacy_system_can_sign(compose->privacy_system)) {
5436 encoding = ENC_QUOTED_PRINTABLE;
5439 debug_print("main text: %zd bytes encoded as %s in %d\n",
5440 strlen(buf), out_codeset, encoding);
5442 /* check for line length limit */
5443 if (action == COMPOSE_WRITE_FOR_SEND &&
5444 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5445 check_line_length(buf, 1000, &line) < 0) {
5449 msg = g_strdup_printf
5450 (_("Line %d exceeds the line length limit (998 bytes).\n"
5451 "The contents of the message might be broken on the way to the delivery.\n"
5453 "Send it anyway?"), line + 1);
5454 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5456 if (aval != G_ALERTALTERNATE) {
5462 if (encoding != ENC_UNKNOWN)
5463 procmime_encode_content(mimetext, encoding);
5465 /* append attachment parts */
5466 if (compose_use_attach(compose) && attach_parts) {
5467 MimeInfo *mimempart;
5468 gchar *boundary = NULL;
5469 mimempart = procmime_mimeinfo_new();
5470 mimempart->content = MIMECONTENT_EMPTY;
5471 mimempart->type = MIMETYPE_MULTIPART;
5472 mimempart->subtype = g_strdup("mixed");
5476 boundary = generate_mime_boundary(NULL);
5477 } while (strstr(buf, boundary) != NULL);
5479 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5482 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5484 g_node_append(mimempart->node, mimetext->node);
5485 g_node_append(mimemsg->node, mimempart->node);
5487 if (compose_add_attachments(compose, mimempart) < 0)
5490 g_node_append(mimemsg->node, mimetext->node);
5494 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5495 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5496 /* extract name and address */
5497 if (strstr(spec, " <") && strstr(spec, ">")) {
5498 from_addr = g_strdup(strrchr(spec, '<')+1);
5499 *(strrchr(from_addr, '>')) = '\0';
5500 from_name = g_strdup(spec);
5501 *(strrchr(from_name, '<')) = '\0';
5508 /* sign message if sending */
5509 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5510 privacy_system_can_sign(compose->privacy_system))
5511 if (!privacy_sign(compose->privacy_system, mimemsg,
5512 compose->account, from_addr)) {
5519 procmime_write_mimeinfo(mimemsg, fp);
5521 procmime_mimeinfo_free_all(mimemsg);
5526 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5528 GtkTextBuffer *buffer;
5529 GtkTextIter start, end;
5534 if ((fp = g_fopen(file, "wb")) == NULL) {
5535 FILE_OP_ERROR(file, "fopen");
5539 /* chmod for security */
5540 if (change_file_mode_rw(fp, file) < 0) {
5541 FILE_OP_ERROR(file, "chmod");
5542 g_warning("can't change file mode\n");
5545 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5546 gtk_text_buffer_get_start_iter(buffer, &start);
5547 gtk_text_buffer_get_end_iter(buffer, &end);
5548 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5550 chars = conv_codeset_strdup
5551 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5554 if (!chars) return -1;
5557 len = strlen(chars);
5558 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5559 FILE_OP_ERROR(file, "fwrite");
5568 if (fclose(fp) == EOF) {
5569 FILE_OP_ERROR(file, "fclose");
5576 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5579 MsgInfo *msginfo = compose->targetinfo;
5581 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5582 if (!msginfo) return -1;
5584 if (!force && MSG_IS_LOCKED(msginfo->flags))
5587 item = msginfo->folder;
5588 cm_return_val_if_fail(item != NULL, -1);
5590 if (procmsg_msg_exist(msginfo) &&
5591 (folder_has_parent_of_type(item, F_QUEUE) ||
5592 folder_has_parent_of_type(item, F_DRAFT)
5593 || msginfo == compose->autosaved_draft)) {
5594 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5595 g_warning("can't remove the old message\n");
5598 debug_print("removed reedit target %d\n", msginfo->msgnum);
5605 static void compose_remove_draft(Compose *compose)
5608 MsgInfo *msginfo = compose->targetinfo;
5609 drafts = account_get_special_folder(compose->account, F_DRAFT);
5611 if (procmsg_msg_exist(msginfo)) {
5612 folder_item_remove_msg(drafts, msginfo->msgnum);
5617 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5618 gboolean remove_reedit_target)
5620 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5623 static gboolean compose_warn_encryption(Compose *compose)
5625 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5626 AlertValue val = G_ALERTALTERNATE;
5628 if (warning == NULL)
5631 val = alertpanel_full(_("Encryption warning"), warning,
5632 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5633 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5634 if (val & G_ALERTDISABLE) {
5635 val &= ~G_ALERTDISABLE;
5636 if (val == G_ALERTALTERNATE)
5637 privacy_inhibit_encrypt_warning(compose->privacy_system,
5641 if (val == G_ALERTALTERNATE) {
5648 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5649 gchar **msgpath, gboolean check_subject,
5650 gboolean remove_reedit_target)
5657 static gboolean lock = FALSE;
5658 PrefsAccount *mailac = NULL, *newsac = NULL;
5659 gboolean err = FALSE;
5661 debug_print("queueing message...\n");
5662 cm_return_val_if_fail(compose->account != NULL, -1);
5666 if (compose_check_entries(compose, check_subject) == FALSE) {
5668 if (compose->batch) {
5669 gtk_widget_show_all(compose->window);
5674 if (!compose->to_list && !compose->newsgroup_list) {
5675 g_warning("can't get recipient list.");
5680 if (compose->to_list) {
5681 if (compose->account->protocol != A_NNTP)
5682 mailac = compose->account;
5683 else if (cur_account && cur_account->protocol != A_NNTP)
5684 mailac = cur_account;
5685 else if (!(mailac = compose_current_mail_account())) {
5687 alertpanel_error(_("No account for sending mails available!"));
5692 if (compose->newsgroup_list) {
5693 if (compose->account->protocol == A_NNTP)
5694 newsac = compose->account;
5697 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5702 /* write queue header */
5703 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5704 G_DIR_SEPARATOR, compose, (guint) rand());
5705 debug_print("queuing to %s\n", tmp);
5706 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5707 FILE_OP_ERROR(tmp, "fopen");
5713 if (change_file_mode_rw(fp, tmp) < 0) {
5714 FILE_OP_ERROR(tmp, "chmod");
5715 g_warning("can't change file mode\n");
5718 /* queueing variables */
5719 err |= (fprintf(fp, "AF:\n") < 0);
5720 err |= (fprintf(fp, "NF:0\n") < 0);
5721 err |= (fprintf(fp, "PS:10\n") < 0);
5722 err |= (fprintf(fp, "SRH:1\n") < 0);
5723 err |= (fprintf(fp, "SFN:\n") < 0);
5724 err |= (fprintf(fp, "DSR:\n") < 0);
5726 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5728 err |= (fprintf(fp, "MID:\n") < 0);
5729 err |= (fprintf(fp, "CFG:\n") < 0);
5730 err |= (fprintf(fp, "PT:0\n") < 0);
5731 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5732 err |= (fprintf(fp, "RQ:\n") < 0);
5734 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5736 err |= (fprintf(fp, "SSV:\n") < 0);
5738 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5740 err |= (fprintf(fp, "NSV:\n") < 0);
5741 err |= (fprintf(fp, "SSH:\n") < 0);
5742 /* write recepient list */
5743 if (compose->to_list) {
5744 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5745 for (cur = compose->to_list->next; cur != NULL;
5747 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5748 err |= (fprintf(fp, "\n") < 0);
5750 /* write newsgroup list */
5751 if (compose->newsgroup_list) {
5752 err |= (fprintf(fp, "NG:") < 0);
5753 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5754 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5755 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5756 err |= (fprintf(fp, "\n") < 0);
5758 /* Sylpheed account IDs */
5760 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5762 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5765 if (compose->privacy_system != NULL) {
5766 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5767 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5768 if (compose->use_encryption) {
5770 if (!compose_warn_encryption(compose)) {
5777 if (mailac && mailac->encrypt_to_self) {
5778 GSList *tmp_list = g_slist_copy(compose->to_list);
5779 tmp_list = g_slist_append(tmp_list, compose->account->address);
5780 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5781 g_slist_free(tmp_list);
5783 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5785 if (encdata != NULL) {
5786 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5787 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5788 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5790 } /* else we finally dont want to encrypt */
5792 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5793 /* and if encdata was null, it means there's been a problem in
5805 /* Save copy folder */
5806 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5807 gchar *savefolderid;
5809 savefolderid = compose_get_save_to(compose);
5810 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5811 g_free(savefolderid);
5813 /* Save copy folder */
5814 if (compose->return_receipt) {
5815 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5817 /* Message-ID of message replying to */
5818 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5821 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5822 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5825 /* Message-ID of message forwarding to */
5826 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5829 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5830 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5834 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5835 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5837 /* end of headers */
5838 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5840 if (compose->redirect_filename != NULL) {
5841 if (compose_redirect_write_to_file(compose, fp) < 0) {
5850 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5855 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5859 g_warning("failed to write queue message\n");
5866 if (fclose(fp) == EOF) {
5867 FILE_OP_ERROR(tmp, "fclose");
5874 if (item && *item) {
5877 queue = account_get_special_folder(compose->account, F_QUEUE);
5880 g_warning("can't find queue folder\n");
5886 folder_item_scan(queue);
5887 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5888 g_warning("can't queue the message\n");
5895 if (msgpath == NULL) {
5901 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5902 compose_remove_reedit_target(compose, FALSE);
5905 if ((msgnum != NULL) && (item != NULL)) {
5913 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5916 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5918 struct stat statbuf;
5919 gchar *type, *subtype;
5920 GtkTreeModel *model;
5923 model = gtk_tree_view_get_model(tree_view);
5925 if (!gtk_tree_model_get_iter_first(model, &iter))
5928 gtk_tree_model_get(model, &iter,
5932 if (!is_file_exist(ainfo->file)) {
5933 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5934 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5935 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5937 if (val == G_ALERTDEFAULT) {
5942 mimepart = procmime_mimeinfo_new();
5943 mimepart->content = MIMECONTENT_FILE;
5944 mimepart->data.filename = g_strdup(ainfo->file);
5945 mimepart->tmp = FALSE; /* or we destroy our attachment */
5946 mimepart->offset = 0;
5948 g_stat(ainfo->file, &statbuf);
5949 mimepart->length = statbuf.st_size;
5951 type = g_strdup(ainfo->content_type);
5953 if (!strchr(type, '/')) {
5955 type = g_strdup("application/octet-stream");
5958 subtype = strchr(type, '/') + 1;
5959 *(subtype - 1) = '\0';
5960 mimepart->type = procmime_get_media_type(type);
5961 mimepart->subtype = g_strdup(subtype);
5964 if (mimepart->type == MIMETYPE_MESSAGE &&
5965 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5966 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5969 if (mimepart->type == MIMETYPE_APPLICATION &&
5970 !strcmp2(mimepart->subtype, "octet-stream"))
5971 g_hash_table_insert(mimepart->typeparameters,
5972 g_strdup("name"), g_strdup(ainfo->name));
5973 g_hash_table_insert(mimepart->dispositionparameters,
5974 g_strdup("filename"), g_strdup(ainfo->name));
5975 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5979 if (compose->use_signing) {
5980 if (ainfo->encoding == ENC_7BIT)
5981 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5982 else if (ainfo->encoding == ENC_8BIT)
5983 ainfo->encoding = ENC_BASE64;
5986 procmime_encode_content(mimepart, ainfo->encoding);
5988 g_node_append(parent->node, mimepart->node);
5989 } while (gtk_tree_model_iter_next(model, &iter));
5994 #define IS_IN_CUSTOM_HEADER(header) \
5995 (compose->account->add_customhdr && \
5996 custom_header_find(compose->account->customhdr_list, header) != NULL)
5998 static void compose_add_headerfield_from_headerlist(Compose *compose,
6000 const gchar *fieldname,
6001 const gchar *seperator)
6003 gchar *str, *fieldname_w_colon;
6004 gboolean add_field = FALSE;
6006 ComposeHeaderEntry *headerentry;
6007 const gchar *headerentryname;
6008 const gchar *trans_fieldname;
6011 if (IS_IN_CUSTOM_HEADER(fieldname))
6014 debug_print("Adding %s-fields\n", fieldname);
6016 fieldstr = g_string_sized_new(64);
6018 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6019 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6021 for (list = compose->header_list; list; list = list->next) {
6022 headerentry = ((ComposeHeaderEntry *)list->data);
6023 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6025 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6026 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6028 if (str[0] != '\0') {
6030 g_string_append(fieldstr, seperator);
6031 g_string_append(fieldstr, str);
6040 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6041 compose_convert_header
6042 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6043 strlen(fieldname) + 2, TRUE);
6044 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6048 g_free(fieldname_w_colon);
6049 g_string_free(fieldstr, TRUE);
6054 static gchar *compose_get_header(Compose *compose)
6056 gchar buf[BUFFSIZE];
6057 const gchar *entry_str;
6061 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6063 gchar *from_name = NULL, *from_address = NULL;
6066 cm_return_val_if_fail(compose->account != NULL, NULL);
6067 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6069 header = g_string_sized_new(64);
6072 get_rfc822_date(buf, sizeof(buf));
6073 g_string_append_printf(header, "Date: %s\n", buf);
6077 if (compose->account->name && *compose->account->name) {
6079 QUOTE_IF_REQUIRED(buf, compose->account->name);
6080 tmp = g_strdup_printf("%s <%s>",
6081 buf, compose->account->address);
6083 tmp = g_strdup_printf("%s",
6084 compose->account->address);
6086 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6087 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6089 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6090 from_address = g_strdup(compose->account->address);
6092 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6093 /* extract name and address */
6094 if (strstr(spec, " <") && strstr(spec, ">")) {
6095 from_address = g_strdup(strrchr(spec, '<')+1);
6096 *(strrchr(from_address, '>')) = '\0';
6097 from_name = g_strdup(spec);
6098 *(strrchr(from_name, '<')) = '\0';
6101 from_address = g_strdup(spec);
6108 if (from_name && *from_name) {
6109 compose_convert_header
6110 (compose, buf, sizeof(buf), from_name,
6111 strlen("From: "), TRUE);
6112 QUOTE_IF_REQUIRED(name, buf);
6114 g_string_append_printf(header, "From: %s <%s>\n",
6115 name, from_address);
6117 g_string_append_printf(header, "From: %s\n", from_address);
6120 g_free(from_address);
6123 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6126 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6129 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6133 * If this account is a NNTP account remove Bcc header from
6134 * message body since it otherwise will be publicly shown
6136 if (compose->account->protocol != A_NNTP)
6137 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6140 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6142 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6145 compose_convert_header(compose, buf, sizeof(buf), str,
6146 strlen("Subject: "), FALSE);
6147 g_string_append_printf(header, "Subject: %s\n", buf);
6153 if (compose->account->set_domain && compose->account->domain) {
6154 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6155 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6156 g_snprintf(buf, sizeof(buf), "%s",
6157 strchr(compose->account->address, '@') ?
6158 strchr(compose->account->address, '@')+1 :
6159 compose->account->address);
6161 g_snprintf(buf, sizeof(buf), "%s", "");
6164 if (compose->account->gen_msgid) {
6166 if (compose->account->msgid_with_addr) {
6167 addr = compose->account->address;
6169 generate_msgid(buf, sizeof(buf), addr);
6170 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6171 compose->msgid = g_strdup(buf);
6173 compose->msgid = NULL;
6176 if (compose->remove_references == FALSE) {
6178 if (compose->inreplyto && compose->to_list)
6179 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6182 if (compose->references)
6183 g_string_append_printf(header, "References: %s\n", compose->references);
6187 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6190 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6193 if (compose->account->organization &&
6194 strlen(compose->account->organization) &&
6195 !IS_IN_CUSTOM_HEADER("Organization")) {
6196 compose_convert_header(compose, buf, sizeof(buf),
6197 compose->account->organization,
6198 strlen("Organization: "), FALSE);
6199 g_string_append_printf(header, "Organization: %s\n", buf);
6202 /* Program version and system info */
6203 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6204 !compose->newsgroup_list) {
6205 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6207 gtk_major_version, gtk_minor_version, gtk_micro_version,
6210 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6211 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6213 gtk_major_version, gtk_minor_version, gtk_micro_version,
6217 /* custom headers */
6218 if (compose->account->add_customhdr) {
6221 for (cur = compose->account->customhdr_list; cur != NULL;
6223 CustomHeader *chdr = (CustomHeader *)cur->data;
6225 if (custom_header_is_allowed(chdr->name)) {
6226 compose_convert_header
6227 (compose, buf, sizeof(buf),
6228 chdr->value ? chdr->value : "",
6229 strlen(chdr->name) + 2, FALSE);
6230 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6235 /* Automatic Faces and X-Faces */
6236 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6237 g_string_append_printf(header, "X-Face: %s\n", buf);
6239 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6240 g_string_append_printf(header, "X-Face: %s\n", buf);
6242 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6243 g_string_append_printf(header, "Face: %s\n", buf);
6245 else if (get_default_face (buf, sizeof(buf)) == 0) {
6246 g_string_append_printf(header, "Face: %s\n", buf);
6250 switch (compose->priority) {
6251 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6252 "X-Priority: 1 (Highest)\n");
6254 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6255 "X-Priority: 2 (High)\n");
6257 case PRIORITY_NORMAL: break;
6258 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6259 "X-Priority: 4 (Low)\n");
6261 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6262 "X-Priority: 5 (Lowest)\n");
6264 default: debug_print("compose: priority unknown : %d\n",
6268 /* Request Return Receipt */
6269 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6270 if (compose->return_receipt) {
6271 if (compose->account->name
6272 && *compose->account->name) {
6273 compose_convert_header(compose, buf, sizeof(buf),
6274 compose->account->name,
6275 strlen("Disposition-Notification-To: "),
6277 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6279 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6283 /* get special headers */
6284 for (list = compose->header_list; list; list = list->next) {
6285 ComposeHeaderEntry *headerentry;
6288 gchar *headername_wcolon;
6289 const gchar *headername_trans;
6292 gboolean standard_header = FALSE;
6294 headerentry = ((ComposeHeaderEntry *)list->data);
6296 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6298 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6303 if (!strstr(tmp, ":")) {
6304 headername_wcolon = g_strconcat(tmp, ":", NULL);
6305 headername = g_strdup(tmp);
6307 headername_wcolon = g_strdup(tmp);
6308 headername = g_strdup(strtok(tmp, ":"));
6312 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6313 Xstrdup_a(headervalue, entry_str, return NULL);
6314 subst_char(headervalue, '\r', ' ');
6315 subst_char(headervalue, '\n', ' ');
6316 string = std_headers;
6317 while (*string != NULL) {
6318 headername_trans = prefs_common_translated_header_name(*string);
6319 if (!strcmp(headername_trans, headername_wcolon))
6320 standard_header = TRUE;
6323 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6324 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6327 g_free(headername_wcolon);
6331 g_string_free(header, FALSE);
6336 #undef IS_IN_CUSTOM_HEADER
6338 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6339 gint header_len, gboolean addr_field)
6341 gchar *tmpstr = NULL;
6342 const gchar *out_codeset = NULL;
6344 cm_return_if_fail(src != NULL);
6345 cm_return_if_fail(dest != NULL);
6347 if (len < 1) return;
6349 tmpstr = g_strdup(src);
6351 subst_char(tmpstr, '\n', ' ');
6352 subst_char(tmpstr, '\r', ' ');
6355 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6356 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6357 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6362 codeconv_set_strict(TRUE);
6363 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6364 conv_get_charset_str(compose->out_encoding));
6365 codeconv_set_strict(FALSE);
6367 if (!dest || *dest == '\0') {
6368 gchar *test_conv_global_out = NULL;
6369 gchar *test_conv_reply = NULL;
6371 /* automatic mode. be automatic. */
6372 codeconv_set_strict(TRUE);
6374 out_codeset = conv_get_outgoing_charset_str();
6376 debug_print("trying to convert to %s\n", out_codeset);
6377 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6380 if (!test_conv_global_out && compose->orig_charset
6381 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6382 out_codeset = compose->orig_charset;
6383 debug_print("failure; trying to convert to %s\n", out_codeset);
6384 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6387 if (!test_conv_global_out && !test_conv_reply) {
6389 out_codeset = CS_INTERNAL;
6390 debug_print("finally using %s\n", out_codeset);
6392 g_free(test_conv_global_out);
6393 g_free(test_conv_reply);
6394 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6396 codeconv_set_strict(FALSE);
6401 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6405 cm_return_if_fail(user_data != NULL);
6407 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6408 g_strstrip(address);
6409 if (*address != '\0') {
6410 gchar *name = procheader_get_fromname(address);
6411 extract_address(address);
6412 addressbook_add_contact(name, address, NULL, NULL);
6417 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6419 GtkWidget *menuitem;
6422 cm_return_if_fail(menu != NULL);
6423 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6425 menuitem = gtk_separator_menu_item_new();
6426 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6427 gtk_widget_show(menuitem);
6429 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6430 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6432 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6433 g_strstrip(address);
6434 if (*address == '\0') {
6435 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6438 g_signal_connect(G_OBJECT(menuitem), "activate",
6439 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6440 gtk_widget_show(menuitem);
6443 static void compose_create_header_entry(Compose *compose)
6445 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6452 const gchar *header = NULL;
6453 ComposeHeaderEntry *headerentry;
6454 gboolean standard_header = FALSE;
6455 GtkListStore *model;
6457 #if !(GTK_CHECK_VERSION(2,12,0))
6458 GtkTooltips *tips = compose->tooltips;
6461 headerentry = g_new0(ComposeHeaderEntry, 1);
6464 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6465 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6466 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6468 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6470 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6472 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6473 COMPOSE_NEWSGROUPS);
6474 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6476 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6477 COMPOSE_FOLLOWUPTO);
6479 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6480 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6481 G_CALLBACK(compose_grab_focus_cb), compose);
6482 gtk_widget_show(combo);
6483 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6484 compose->header_nextrow, compose->header_nextrow+1,
6485 GTK_SHRINK, GTK_FILL, 0, 0);
6486 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6487 const gchar *last_header_entry = gtk_entry_get_text(
6488 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6490 while (*string != NULL) {
6491 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6492 standard_header = TRUE;
6495 if (standard_header)
6496 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6498 if (!compose->header_last || !standard_header) {
6499 switch(compose->account->protocol) {
6501 header = prefs_common_translated_header_name("Newsgroups:");
6504 header = prefs_common_translated_header_name("To:");
6509 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6511 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6512 G_CALLBACK(compose_grab_focus_cb), compose);
6514 /* Entry field with cleanup button */
6515 button = gtk_button_new();
6516 gtk_button_set_image(GTK_BUTTON(button),
6517 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6518 gtk_widget_show(button);
6519 CLAWS_SET_TIP(button,
6520 _("Delete entry contents"));
6521 entry = gtk_entry_new();
6522 gtk_widget_show(entry);
6523 CLAWS_SET_TIP(entry,
6524 _("Use <tab> to autocomplete from addressbook"));
6525 hbox = gtk_hbox_new (FALSE, 0);
6526 gtk_widget_show(hbox);
6527 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6528 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6529 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6530 compose->header_nextrow, compose->header_nextrow+1,
6531 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6533 g_signal_connect(G_OBJECT(entry), "key-press-event",
6534 G_CALLBACK(compose_headerentry_key_press_event_cb),
6536 g_signal_connect(G_OBJECT(entry), "changed",
6537 G_CALLBACK(compose_headerentry_changed_cb),
6539 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6540 G_CALLBACK(compose_grab_focus_cb), compose);
6542 g_signal_connect(G_OBJECT(button), "clicked",
6543 G_CALLBACK(compose_headerentry_button_clicked_cb),
6547 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6548 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6549 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6550 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6551 G_CALLBACK(compose_header_drag_received_cb),
6553 g_signal_connect(G_OBJECT(entry), "drag-drop",
6554 G_CALLBACK(compose_drag_drop),
6556 g_signal_connect(G_OBJECT(entry), "populate-popup",
6557 G_CALLBACK(compose_entry_popup_extend),
6560 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6562 headerentry->compose = compose;
6563 headerentry->combo = combo;
6564 headerentry->entry = entry;
6565 headerentry->button = button;
6566 headerentry->hbox = hbox;
6567 headerentry->headernum = compose->header_nextrow;
6568 headerentry->type = PREF_NONE;
6570 compose->header_nextrow++;
6571 compose->header_last = headerentry;
6572 compose->header_list =
6573 g_slist_append(compose->header_list,
6577 static void compose_add_header_entry(Compose *compose, const gchar *header,
6578 gchar *text, ComposePrefType pref_type)
6580 ComposeHeaderEntry *last_header = compose->header_last;
6581 gchar *tmp = g_strdup(text), *email;
6582 gboolean replyto_hdr;
6584 replyto_hdr = (!strcasecmp(header,
6585 prefs_common_translated_header_name("Reply-To:")) ||
6587 prefs_common_translated_header_name("Followup-To:")) ||
6589 prefs_common_translated_header_name("In-Reply-To:")));
6591 extract_address(tmp);
6592 email = g_utf8_strdown(tmp, -1);
6594 if (replyto_hdr == FALSE &&
6595 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6597 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6598 header, text, (gint) pref_type);
6604 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6605 gtk_entry_set_text(GTK_ENTRY(
6606 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6608 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6609 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6610 last_header->type = pref_type;
6612 if (replyto_hdr == FALSE)
6613 g_hash_table_insert(compose->email_hashtable, email,
6614 GUINT_TO_POINTER(1));
6621 static void compose_destroy_headerentry(Compose *compose,
6622 ComposeHeaderEntry *headerentry)
6624 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6627 extract_address(text);
6628 email = g_utf8_strdown(text, -1);
6629 g_hash_table_remove(compose->email_hashtable, email);
6633 gtk_widget_destroy(headerentry->combo);
6634 gtk_widget_destroy(headerentry->entry);
6635 gtk_widget_destroy(headerentry->button);
6636 gtk_widget_destroy(headerentry->hbox);
6637 g_free(headerentry);
6640 static void compose_remove_header_entries(Compose *compose)
6643 for (list = compose->header_list; list; list = list->next)
6644 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6646 compose->header_last = NULL;
6647 g_slist_free(compose->header_list);
6648 compose->header_list = NULL;
6649 compose->header_nextrow = 1;
6650 compose_create_header_entry(compose);
6653 static GtkWidget *compose_create_header(Compose *compose)
6655 GtkWidget *from_optmenu_hbox;
6656 GtkWidget *header_scrolledwin;
6657 GtkWidget *header_table;
6661 /* header labels and entries */
6662 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6663 gtk_widget_show(header_scrolledwin);
6664 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6666 header_table = gtk_table_new(2, 2, FALSE);
6667 gtk_widget_show(header_table);
6668 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6669 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6670 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6673 /* option menu for selecting accounts */
6674 from_optmenu_hbox = compose_account_option_menu_create(compose);
6675 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6676 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6679 compose->header_table = header_table;
6680 compose->header_list = NULL;
6681 compose->header_nextrow = count;
6683 compose_create_header_entry(compose);
6685 compose->table = NULL;
6687 return header_scrolledwin ;
6690 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6692 Compose *compose = (Compose *)data;
6693 GdkEventButton event;
6696 event.time = gtk_get_current_event_time();
6698 return attach_button_pressed(compose->attach_clist, &event, compose);
6701 static GtkWidget *compose_create_attach(Compose *compose)
6703 GtkWidget *attach_scrwin;
6704 GtkWidget *attach_clist;
6706 GtkListStore *store;
6707 GtkCellRenderer *renderer;
6708 GtkTreeViewColumn *column;
6709 GtkTreeSelection *selection;
6711 /* attachment list */
6712 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6713 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6714 GTK_POLICY_AUTOMATIC,
6715 GTK_POLICY_AUTOMATIC);
6716 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6718 store = gtk_list_store_new(N_ATTACH_COLS,
6723 G_TYPE_AUTO_POINTER,
6725 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6726 (GTK_TREE_MODEL(store)));
6727 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6728 g_object_unref(store);
6730 renderer = gtk_cell_renderer_text_new();
6731 column = gtk_tree_view_column_new_with_attributes
6732 (_("Mime type"), renderer, "text",
6733 COL_MIMETYPE, NULL);
6734 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6736 renderer = gtk_cell_renderer_text_new();
6737 column = gtk_tree_view_column_new_with_attributes
6738 (_("Size"), renderer, "text",
6740 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6742 renderer = gtk_cell_renderer_text_new();
6743 column = gtk_tree_view_column_new_with_attributes
6744 (_("Name"), renderer, "text",
6746 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6748 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6749 prefs_common.use_stripes_everywhere);
6750 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6751 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6753 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6754 G_CALLBACK(attach_selected), compose);
6755 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6756 G_CALLBACK(attach_button_pressed), compose);
6758 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6759 G_CALLBACK(popup_attach_button_pressed), compose);
6761 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6762 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6763 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6764 G_CALLBACK(popup_attach_button_pressed), compose);
6766 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6767 G_CALLBACK(attach_key_pressed), compose);
6770 gtk_drag_dest_set(attach_clist,
6771 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6772 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6773 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6774 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6775 G_CALLBACK(compose_attach_drag_received_cb),
6777 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6778 G_CALLBACK(compose_drag_drop),
6781 compose->attach_scrwin = attach_scrwin;
6782 compose->attach_clist = attach_clist;
6784 return attach_scrwin;
6787 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6788 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6790 static GtkWidget *compose_create_others(Compose *compose)
6793 GtkWidget *savemsg_checkbtn;
6794 GtkWidget *savemsg_combo;
6795 GtkWidget *savemsg_select;
6798 gchar *folderidentifier;
6800 /* Table for settings */
6801 table = gtk_table_new(3, 1, FALSE);
6802 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6803 gtk_widget_show(table);
6804 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6807 /* Save Message to folder */
6808 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6809 gtk_widget_show(savemsg_checkbtn);
6810 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6811 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6812 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6814 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6815 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6817 savemsg_combo = gtk_combo_box_entry_new_text();
6818 compose->savemsg_checkbtn = savemsg_checkbtn;
6819 compose->savemsg_combo = savemsg_combo;
6820 gtk_widget_show(savemsg_combo);
6822 if (prefs_common.compose_save_to_history)
6823 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6824 prefs_common.compose_save_to_history);
6826 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6827 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6828 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6829 G_CALLBACK(compose_grab_focus_cb), compose);
6830 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6831 folderidentifier = folder_item_get_identifier(account_get_special_folder
6832 (compose->account, F_OUTBOX));
6833 compose_set_save_to(compose, folderidentifier);
6834 g_free(folderidentifier);
6837 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6838 gtk_widget_show(savemsg_select);
6839 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6840 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6841 G_CALLBACK(compose_savemsg_select_cb),
6849 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6851 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6852 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6855 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6860 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6863 path = folder_item_get_identifier(dest);
6865 compose_set_save_to(compose, path);
6869 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6870 GdkAtom clip, GtkTextIter *insert_place);
6873 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6877 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6879 if (event->button == 3) {
6881 GtkTextIter sel_start, sel_end;
6882 gboolean stuff_selected;
6884 /* move the cursor to allow GtkAspell to check the word
6885 * under the mouse */
6886 if (event->x && event->y) {
6887 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6888 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6890 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6893 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6894 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6897 stuff_selected = gtk_text_buffer_get_selection_bounds(
6899 &sel_start, &sel_end);
6901 gtk_text_buffer_place_cursor (buffer, &iter);
6902 /* reselect stuff */
6904 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6905 gtk_text_buffer_select_range(buffer,
6906 &sel_start, &sel_end);
6908 return FALSE; /* pass the event so that the right-click goes through */
6911 if (event->button == 2) {
6916 /* get the middle-click position to paste at the correct place */
6917 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6918 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6920 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6923 entry_paste_clipboard(compose, text,
6924 prefs_common.linewrap_pastes,
6925 GDK_SELECTION_PRIMARY, &iter);
6933 static void compose_spell_menu_changed(void *data)
6935 Compose *compose = (Compose *)data;
6937 GtkWidget *menuitem;
6938 GtkWidget *parent_item;
6939 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6942 if (compose->gtkaspell == NULL)
6945 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6946 "/Menu/Spelling/Options");
6948 /* setting the submenu removes /Spelling/Options from the factory
6949 * so we need to save it */
6951 if (parent_item == NULL) {
6952 parent_item = compose->aspell_options_menu;
6953 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6955 compose->aspell_options_menu = parent_item;
6957 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6959 spell_menu = g_slist_reverse(spell_menu);
6960 for (items = spell_menu;
6961 items; items = items->next) {
6962 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6963 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6964 gtk_widget_show(GTK_WIDGET(menuitem));
6966 g_slist_free(spell_menu);
6968 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6969 gtk_widget_show(parent_item);
6972 static void compose_dict_changed(void *data)
6974 Compose *compose = (Compose *) data;
6976 if(compose->gtkaspell &&
6977 compose->gtkaspell->recheck_when_changing_dict == FALSE)
6980 gtkaspell_highlight_all(compose->gtkaspell);
6981 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6985 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6987 Compose *compose = (Compose *)data;
6988 GdkEventButton event;
6991 event.time = gtk_get_current_event_time();
6995 return text_clicked(compose->text, &event, compose);
6998 static gboolean compose_force_window_origin = TRUE;
6999 static Compose *compose_create(PrefsAccount *account,
7008 GtkWidget *handlebox;
7010 GtkWidget *notebook;
7012 GtkWidget *attach_hbox;
7013 GtkWidget *attach_lab1;
7014 GtkWidget *attach_lab2;
7019 GtkWidget *subject_hbox;
7020 GtkWidget *subject_frame;
7021 GtkWidget *subject_entry;
7025 GtkWidget *edit_vbox;
7026 GtkWidget *ruler_hbox;
7028 GtkWidget *scrolledwin;
7030 GtkTextBuffer *buffer;
7031 GtkClipboard *clipboard;
7034 UndoMain *undostruct;
7036 gchar *titles[N_ATTACH_COLS];
7037 GtkWidget *popupmenu;
7038 GtkWidget *tmpl_menu;
7039 GtkActionGroup *action_group = NULL;
7042 GtkAspell * gtkaspell = NULL;
7045 static GdkGeometry geometry;
7047 cm_return_val_if_fail(account != NULL, NULL);
7049 debug_print("Creating compose window...\n");
7050 compose = g_new0(Compose, 1);
7052 titles[COL_MIMETYPE] = _("MIME type");
7053 titles[COL_SIZE] = _("Size");
7054 titles[COL_NAME] = _("Name");
7056 compose->batch = batch;
7057 compose->account = account;
7058 compose->folder = folder;
7060 compose->mutex = g_mutex_new();
7061 compose->set_cursor_pos = -1;
7063 #if !(GTK_CHECK_VERSION(2,12,0))
7064 compose->tooltips = tips;
7067 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7069 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7070 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7072 if (!geometry.max_width) {
7073 geometry.max_width = gdk_screen_width();
7074 geometry.max_height = gdk_screen_height();
7077 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7078 &geometry, GDK_HINT_MAX_SIZE);
7079 if (!geometry.min_width) {
7080 geometry.min_width = 600;
7081 geometry.min_height = 440;
7083 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7084 &geometry, GDK_HINT_MIN_SIZE);
7086 #ifndef GENERIC_UMPC
7087 if (compose_force_window_origin)
7088 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7089 prefs_common.compose_y);
7091 g_signal_connect(G_OBJECT(window), "delete_event",
7092 G_CALLBACK(compose_delete_cb), compose);
7093 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7094 gtk_widget_realize(window);
7096 gtkut_widget_set_composer_icon(window);
7098 vbox = gtk_vbox_new(FALSE, 0);
7099 gtk_container_add(GTK_CONTAINER(window), vbox);
7101 compose->ui_manager = gtk_ui_manager_new();
7102 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7103 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7104 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7105 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7106 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7107 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7108 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7109 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7110 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7111 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7114 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7116 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7119 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7120 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7124 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7129 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7131 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7133 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7134 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7231 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)
7232 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)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7235 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7238 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)
7239 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)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7244 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)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7248 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)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7254 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)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7260 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)
7261 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)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7266 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7268 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7274 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)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7277 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7292 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7293 gtk_widget_show_all(menubar);
7295 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7297 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7299 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7302 if (prefs_common.toolbar_detachable) {
7303 handlebox = gtk_handle_box_new();
7305 handlebox = gtk_hbox_new(FALSE, 0);
7307 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7309 gtk_widget_realize(handlebox);
7311 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7314 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7318 vbox2 = gtk_vbox_new(FALSE, 2);
7319 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7320 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7323 notebook = gtk_notebook_new();
7324 gtk_widget_set_size_request(notebook, -1, 130);
7325 gtk_widget_show(notebook);
7327 /* header labels and entries */
7328 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7329 compose_create_header(compose),
7330 gtk_label_new_with_mnemonic(_("Hea_der")));
7331 /* attachment list */
7332 attach_hbox = gtk_hbox_new(FALSE, 0);
7333 gtk_widget_show(attach_hbox);
7335 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7336 gtk_widget_show(attach_lab1);
7337 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7339 attach_lab2 = gtk_label_new("");
7340 gtk_widget_show(attach_lab2);
7341 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7343 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7344 compose_create_attach(compose),
7347 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7348 compose_create_others(compose),
7349 gtk_label_new_with_mnemonic(_("Othe_rs")));
7352 subject_hbox = gtk_hbox_new(FALSE, 0);
7353 gtk_widget_show(subject_hbox);
7355 subject_frame = gtk_frame_new(NULL);
7356 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7357 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7358 gtk_widget_show(subject_frame);
7360 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7361 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7362 gtk_widget_show(subject);
7364 label = gtk_label_new(_("Subject:"));
7365 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7366 gtk_widget_show(label);
7369 subject_entry = claws_spell_entry_new();
7371 subject_entry = gtk_entry_new();
7373 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7374 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7375 G_CALLBACK(compose_grab_focus_cb), compose);
7376 gtk_widget_show(subject_entry);
7377 compose->subject_entry = subject_entry;
7378 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7380 edit_vbox = gtk_vbox_new(FALSE, 0);
7382 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7385 ruler_hbox = gtk_hbox_new(FALSE, 0);
7386 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7388 ruler = gtk_shruler_new();
7389 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7390 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7394 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7395 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7396 GTK_POLICY_AUTOMATIC,
7397 GTK_POLICY_AUTOMATIC);
7398 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7400 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7401 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7403 text = gtk_text_view_new();
7404 if (prefs_common.show_compose_margin) {
7405 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7406 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7408 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7409 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7410 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7411 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7412 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7414 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7416 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7417 G_CALLBACK(compose_edit_size_alloc),
7419 g_signal_connect(G_OBJECT(buffer), "changed",
7420 G_CALLBACK(compose_changed_cb), compose);
7421 g_signal_connect(G_OBJECT(text), "grab_focus",
7422 G_CALLBACK(compose_grab_focus_cb), compose);
7423 g_signal_connect(G_OBJECT(buffer), "insert_text",
7424 G_CALLBACK(text_inserted), compose);
7425 g_signal_connect(G_OBJECT(text), "button_press_event",
7426 G_CALLBACK(text_clicked), compose);
7428 g_signal_connect(G_OBJECT(text), "popup-menu",
7429 G_CALLBACK(compose_popup_menu), compose);
7431 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7432 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7433 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7434 G_CALLBACK(compose_popup_menu), compose);
7436 g_signal_connect(G_OBJECT(subject_entry), "changed",
7437 G_CALLBACK(compose_changed_cb), compose);
7440 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7441 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7442 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7443 g_signal_connect(G_OBJECT(text), "drag_data_received",
7444 G_CALLBACK(compose_insert_drag_received_cb),
7446 g_signal_connect(G_OBJECT(text), "drag-drop",
7447 G_CALLBACK(compose_drag_drop),
7449 gtk_widget_show_all(vbox);
7451 /* pane between attach clist and text */
7452 paned = gtk_vpaned_new();
7453 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7455 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7456 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7458 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7460 gtk_paned_add1(GTK_PANED(paned), notebook);
7461 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7462 gtk_widget_show_all(paned);
7465 if (prefs_common.textfont) {
7466 PangoFontDescription *font_desc;
7468 font_desc = pango_font_description_from_string
7469 (prefs_common.textfont);
7471 gtk_widget_modify_font(text, font_desc);
7472 pango_font_description_free(font_desc);
7476 gtk_action_group_add_actions(action_group, compose_popup_entries,
7477 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7478 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7479 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7480 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7481 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7485 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7487 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7488 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7489 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7491 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7493 undostruct = undo_init(text);
7494 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7497 address_completion_start(window);
7499 compose->window = window;
7500 compose->vbox = vbox;
7501 compose->menubar = menubar;
7502 compose->handlebox = handlebox;
7504 compose->vbox2 = vbox2;
7506 compose->paned = paned;
7508 compose->attach_label = attach_lab2;
7510 compose->notebook = notebook;
7511 compose->edit_vbox = edit_vbox;
7512 compose->ruler_hbox = ruler_hbox;
7513 compose->ruler = ruler;
7514 compose->scrolledwin = scrolledwin;
7515 compose->text = text;
7517 compose->focused_editable = NULL;
7519 compose->popupmenu = popupmenu;
7521 compose->tmpl_menu = tmpl_menu;
7523 compose->mode = mode;
7524 compose->rmode = mode;
7526 compose->targetinfo = NULL;
7527 compose->replyinfo = NULL;
7528 compose->fwdinfo = NULL;
7530 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7531 g_str_equal, (GDestroyNotify) g_free, NULL);
7533 compose->replyto = NULL;
7535 compose->bcc = NULL;
7536 compose->followup_to = NULL;
7538 compose->ml_post = NULL;
7540 compose->inreplyto = NULL;
7541 compose->references = NULL;
7542 compose->msgid = NULL;
7543 compose->boundary = NULL;
7545 compose->autowrap = prefs_common.autowrap;
7546 compose->autoindent = prefs_common.auto_indent;
7547 compose->use_signing = FALSE;
7548 compose->use_encryption = FALSE;
7549 compose->privacy_system = NULL;
7551 compose->modified = FALSE;
7553 compose->return_receipt = FALSE;
7555 compose->to_list = NULL;
7556 compose->newsgroup_list = NULL;
7558 compose->undostruct = undostruct;
7560 compose->sig_str = NULL;
7562 compose->exteditor_file = NULL;
7563 compose->exteditor_pid = -1;
7564 compose->exteditor_tag = -1;
7565 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7569 if (mode != COMPOSE_REDIRECT) {
7570 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7571 strcmp(prefs_common.dictionary, "")) {
7572 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7573 prefs_common.alt_dictionary,
7574 conv_get_locale_charset_str(),
7575 prefs_common.misspelled_col,
7576 prefs_common.check_while_typing,
7577 prefs_common.recheck_when_changing_dict,
7578 prefs_common.use_alternate,
7579 prefs_common.use_both_dicts,
7580 GTK_TEXT_VIEW(text),
7581 GTK_WINDOW(compose->window),
7582 compose_dict_changed,
7583 compose_spell_menu_changed,
7586 alertpanel_error(_("Spell checker could not "
7588 gtkaspell_checkers_strerror());
7589 gtkaspell_checkers_reset_error();
7591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7595 compose->gtkaspell = gtkaspell;
7596 compose_spell_menu_changed(compose);
7597 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7600 compose_select_account(compose, account, TRUE);
7602 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7603 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7605 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7606 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7608 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7609 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7611 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7612 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7614 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7615 if (account->protocol != A_NNTP)
7616 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7617 prefs_common_translated_header_name("To:"));
7619 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7620 prefs_common_translated_header_name("Newsgroups:"));
7622 addressbook_set_target_compose(compose);
7624 if (mode != COMPOSE_REDIRECT)
7625 compose_set_template_menu(compose);
7627 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7630 compose_list = g_list_append(compose_list, compose);
7632 if (!prefs_common.show_ruler)
7633 gtk_widget_hide(ruler_hbox);
7635 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7638 compose->priority = PRIORITY_NORMAL;
7639 compose_update_priority_menu_item(compose);
7641 compose_set_out_encoding(compose);
7644 compose_update_actions_menu(compose);
7646 /* Privacy Systems menu */
7647 compose_update_privacy_systems_menu(compose);
7649 activate_privacy_system(compose, account, TRUE);
7650 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7652 gtk_widget_realize(window);
7654 gtk_widget_show(window);
7656 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7657 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7664 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7669 GtkWidget *optmenubox;
7672 GtkWidget *from_name = NULL;
7673 #if !(GTK_CHECK_VERSION(2,12,0))
7674 GtkTooltips *tips = compose->tooltips;
7677 gint num = 0, def_menu = 0;
7679 accounts = account_get_list();
7680 cm_return_val_if_fail(accounts != NULL, NULL);
7682 optmenubox = gtk_event_box_new();
7683 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7684 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7686 hbox = gtk_hbox_new(FALSE, 6);
7687 from_name = gtk_entry_new();
7689 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7690 G_CALLBACK(compose_grab_focus_cb), compose);
7692 for (; accounts != NULL; accounts = accounts->next, num++) {
7693 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7694 gchar *name, *from = NULL;
7696 if (ac == compose->account) def_menu = num;
7698 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7701 if (ac == compose->account) {
7702 if (ac->name && *ac->name) {
7704 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7705 from = g_strdup_printf("%s <%s>",
7707 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7709 from = g_strdup_printf("%s",
7711 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7714 COMBOBOX_ADD(menu, name, ac->account_id);
7719 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7721 g_signal_connect(G_OBJECT(optmenu), "changed",
7722 G_CALLBACK(account_activated),
7724 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7725 G_CALLBACK(compose_entry_popup_extend),
7728 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7729 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7731 CLAWS_SET_TIP(optmenubox,
7732 _("Account to use for this email"));
7733 CLAWS_SET_TIP(from_name,
7734 _("Sender address to be used"));
7736 compose->account_combo = optmenu;
7737 compose->from_name = from_name;
7742 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7744 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7745 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7746 Compose *compose = (Compose *) data;
7748 compose->priority = value;
7752 static void compose_reply_change_mode(Compose *compose,
7755 gboolean was_modified = compose->modified;
7757 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7759 cm_return_if_fail(compose->replyinfo != NULL);
7761 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7763 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7765 if (action == COMPOSE_REPLY_TO_ALL)
7767 if (action == COMPOSE_REPLY_TO_SENDER)
7769 if (action == COMPOSE_REPLY_TO_LIST)
7772 compose_remove_header_entries(compose);
7773 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7774 if (compose->account->set_autocc && compose->account->auto_cc)
7775 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7777 if (compose->account->set_autobcc && compose->account->auto_bcc)
7778 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7780 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7781 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7782 compose_show_first_last_header(compose, TRUE);
7783 compose->modified = was_modified;
7784 compose_set_title(compose);
7787 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7789 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7790 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7791 Compose *compose = (Compose *) data;
7794 compose_reply_change_mode(compose, value);
7797 static void compose_update_priority_menu_item(Compose * compose)
7799 GtkWidget *menuitem = NULL;
7800 switch (compose->priority) {
7801 case PRIORITY_HIGHEST:
7802 menuitem = gtk_ui_manager_get_widget
7803 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7806 menuitem = gtk_ui_manager_get_widget
7807 (compose->ui_manager, "/Menu/Options/Priority/High");
7809 case PRIORITY_NORMAL:
7810 menuitem = gtk_ui_manager_get_widget
7811 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7814 menuitem = gtk_ui_manager_get_widget
7815 (compose->ui_manager, "/Menu/Options/Priority/Low");
7817 case PRIORITY_LOWEST:
7818 menuitem = gtk_ui_manager_get_widget
7819 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7822 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7825 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7827 Compose *compose = (Compose *) data;
7829 gboolean can_sign = FALSE, can_encrypt = FALSE;
7831 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7833 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7836 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7837 g_free(compose->privacy_system);
7838 compose->privacy_system = NULL;
7839 if (systemid != NULL) {
7840 compose->privacy_system = g_strdup(systemid);
7842 can_sign = privacy_system_can_sign(systemid);
7843 can_encrypt = privacy_system_can_encrypt(systemid);
7846 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7848 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7849 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7852 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7854 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7855 GtkWidget *menuitem = NULL;
7857 gboolean can_sign = FALSE, can_encrypt = FALSE;
7858 gboolean found = FALSE;
7860 if (compose->privacy_system != NULL) {
7862 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7863 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7864 cm_return_if_fail(menuitem != NULL);
7866 amenu = GTK_MENU_SHELL(menuitem)->children;
7868 while (amenu != NULL) {
7869 GList *alist = amenu->next;
7871 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7872 if (systemid != NULL) {
7873 if (strcmp(systemid, compose->privacy_system) == 0 &&
7874 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7875 menuitem = GTK_WIDGET(amenu->data);
7877 can_sign = privacy_system_can_sign(systemid);
7878 can_encrypt = privacy_system_can_encrypt(systemid);
7882 } else if (strlen(compose->privacy_system) == 0 &&
7883 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7884 menuitem = GTK_WIDGET(amenu->data);
7887 can_encrypt = FALSE;
7894 if (menuitem != NULL)
7895 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7897 if (warn && !found && strlen(compose->privacy_system)) {
7898 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7899 "will not be able to sign or encrypt this message."),
7900 compose->privacy_system);
7904 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7905 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7908 static void compose_set_out_encoding(Compose *compose)
7910 CharSet out_encoding;
7911 const gchar *branch = NULL;
7912 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7914 switch(out_encoding) {
7915 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7916 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7917 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7918 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7919 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7920 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7921 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7922 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7923 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7924 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7925 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7926 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7927 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7928 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7929 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7930 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7931 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7932 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7933 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7934 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7935 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7936 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7937 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7938 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7939 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7940 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7941 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7942 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7943 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7944 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7945 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7946 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7947 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7949 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7952 static void compose_set_template_menu(Compose *compose)
7954 GSList *tmpl_list, *cur;
7958 tmpl_list = template_get_config();
7960 menu = gtk_menu_new();
7962 gtk_menu_set_accel_group (GTK_MENU (menu),
7963 gtk_ui_manager_get_accel_group(compose->ui_manager));
7964 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7965 Template *tmpl = (Template *)cur->data;
7966 gchar *accel_path = NULL;
7967 item = gtk_menu_item_new_with_label(tmpl->name);
7968 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7969 g_signal_connect(G_OBJECT(item), "activate",
7970 G_CALLBACK(compose_template_activate_cb),
7972 g_object_set_data(G_OBJECT(item), "template", tmpl);
7973 gtk_widget_show(item);
7974 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7975 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7979 gtk_widget_show(menu);
7980 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7983 void compose_update_actions_menu(Compose *compose)
7985 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7988 static void compose_update_privacy_systems_menu(Compose *compose)
7990 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7991 GSList *systems, *cur;
7993 GtkWidget *system_none;
7995 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7996 GtkWidget *privacy_menu = gtk_menu_new();
7998 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7999 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8001 g_signal_connect(G_OBJECT(system_none), "activate",
8002 G_CALLBACK(compose_set_privacy_system_cb), compose);
8004 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8005 gtk_widget_show(system_none);
8007 systems = privacy_get_system_ids();
8008 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8009 gchar *systemid = cur->data;
8011 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8012 widget = gtk_radio_menu_item_new_with_label(group,
8013 privacy_system_get_name(systemid));
8014 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8015 g_strdup(systemid), g_free);
8016 g_signal_connect(G_OBJECT(widget), "activate",
8017 G_CALLBACK(compose_set_privacy_system_cb), compose);
8019 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8020 gtk_widget_show(widget);
8023 g_slist_free(systems);
8024 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8025 gtk_widget_show_all(privacy_menu);
8026 gtk_widget_show_all(privacy_menuitem);
8029 void compose_reflect_prefs_all(void)
8034 for (cur = compose_list; cur != NULL; cur = cur->next) {
8035 compose = (Compose *)cur->data;
8036 compose_set_template_menu(compose);
8040 void compose_reflect_prefs_pixmap_theme(void)
8045 for (cur = compose_list; cur != NULL; cur = cur->next) {
8046 compose = (Compose *)cur->data;
8047 toolbar_update(TOOLBAR_COMPOSE, compose);
8051 static const gchar *compose_quote_char_from_context(Compose *compose)
8053 const gchar *qmark = NULL;
8055 cm_return_val_if_fail(compose != NULL, NULL);
8057 switch (compose->mode) {
8058 /* use forward-specific quote char */
8059 case COMPOSE_FORWARD:
8060 case COMPOSE_FORWARD_AS_ATTACH:
8061 case COMPOSE_FORWARD_INLINE:
8062 if (compose->folder && compose->folder->prefs &&
8063 compose->folder->prefs->forward_with_format)
8064 qmark = compose->folder->prefs->forward_quotemark;
8065 else if (compose->account->forward_with_format)
8066 qmark = compose->account->forward_quotemark;
8068 qmark = prefs_common.fw_quotemark;
8071 /* use reply-specific quote char in all other modes */
8073 if (compose->folder && compose->folder->prefs &&
8074 compose->folder->prefs->reply_with_format)
8075 qmark = compose->folder->prefs->reply_quotemark;
8076 else if (compose->account->reply_with_format)
8077 qmark = compose->account->reply_quotemark;
8079 qmark = prefs_common.quotemark;
8083 if (qmark == NULL || *qmark == '\0')
8089 static void compose_template_apply(Compose *compose, Template *tmpl,
8093 GtkTextBuffer *buffer;
8097 gchar *parsed_str = NULL;
8098 gint cursor_pos = 0;
8099 const gchar *err_msg = _("The body of the template has an error at line %d.");
8102 /* process the body */
8104 text = GTK_TEXT_VIEW(compose->text);
8105 buffer = gtk_text_view_get_buffer(text);
8108 qmark = compose_quote_char_from_context(compose);
8110 if (compose->replyinfo != NULL) {
8113 gtk_text_buffer_set_text(buffer, "", -1);
8114 mark = gtk_text_buffer_get_insert(buffer);
8115 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8117 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8118 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8120 } else if (compose->fwdinfo != NULL) {
8123 gtk_text_buffer_set_text(buffer, "", -1);
8124 mark = gtk_text_buffer_get_insert(buffer);
8125 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8127 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8128 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8131 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8133 GtkTextIter start, end;
8136 gtk_text_buffer_get_start_iter(buffer, &start);
8137 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8138 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8140 /* clear the buffer now */
8142 gtk_text_buffer_set_text(buffer, "", -1);
8144 parsed_str = compose_quote_fmt(compose, dummyinfo,
8145 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8146 procmsg_msginfo_free( dummyinfo );
8152 gtk_text_buffer_set_text(buffer, "", -1);
8153 mark = gtk_text_buffer_get_insert(buffer);
8154 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8157 if (replace && parsed_str && compose->account->auto_sig)
8158 compose_insert_sig(compose, FALSE);
8160 if (replace && parsed_str) {
8161 gtk_text_buffer_get_start_iter(buffer, &iter);
8162 gtk_text_buffer_place_cursor(buffer, &iter);
8166 cursor_pos = quote_fmt_get_cursor_pos();
8167 compose->set_cursor_pos = cursor_pos;
8168 if (cursor_pos == -1)
8170 gtk_text_buffer_get_start_iter(buffer, &iter);
8171 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8172 gtk_text_buffer_place_cursor(buffer, &iter);
8175 /* process the other fields */
8177 compose_template_apply_fields(compose, tmpl);
8178 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8179 quote_fmt_reset_vartable();
8180 compose_changed_cb(NULL, compose);
8183 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8184 gtkaspell_highlight_all(compose->gtkaspell);
8188 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8190 MsgInfo* dummyinfo = NULL;
8191 MsgInfo *msginfo = NULL;
8194 if (compose->replyinfo != NULL)
8195 msginfo = compose->replyinfo;
8196 else if (compose->fwdinfo != NULL)
8197 msginfo = compose->fwdinfo;
8199 dummyinfo = compose_msginfo_new_from_compose(compose);
8200 msginfo = dummyinfo;
8203 if (tmpl->from && *tmpl->from != '\0') {
8205 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8206 compose->gtkaspell);
8208 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8210 quote_fmt_scan_string(tmpl->from);
8213 buf = quote_fmt_get_buffer();
8215 alertpanel_error(_("Template From format error."));
8217 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8221 if (tmpl->to && *tmpl->to != '\0') {
8223 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8224 compose->gtkaspell);
8226 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8228 quote_fmt_scan_string(tmpl->to);
8231 buf = quote_fmt_get_buffer();
8233 alertpanel_error(_("Template To format error."));
8235 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8239 if (tmpl->cc && *tmpl->cc != '\0') {
8241 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8242 compose->gtkaspell);
8244 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8246 quote_fmt_scan_string(tmpl->cc);
8249 buf = quote_fmt_get_buffer();
8251 alertpanel_error(_("Template Cc format error."));
8253 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8257 if (tmpl->bcc && *tmpl->bcc != '\0') {
8259 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8260 compose->gtkaspell);
8262 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8264 quote_fmt_scan_string(tmpl->bcc);
8267 buf = quote_fmt_get_buffer();
8269 alertpanel_error(_("Template Bcc format error."));
8271 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8275 /* process the subject */
8276 if (tmpl->subject && *tmpl->subject != '\0') {
8278 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8279 compose->gtkaspell);
8281 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8283 quote_fmt_scan_string(tmpl->subject);
8286 buf = quote_fmt_get_buffer();
8288 alertpanel_error(_("Template subject format error."));
8290 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8294 procmsg_msginfo_free( dummyinfo );
8297 static void compose_destroy(Compose *compose)
8299 GtkTextBuffer *buffer;
8300 GtkClipboard *clipboard;
8302 compose_list = g_list_remove(compose_list, compose);
8304 if (compose->updating) {
8305 debug_print("danger, not destroying anything now\n");
8306 compose->deferred_destroy = TRUE;
8309 /* NOTE: address_completion_end() does nothing with the window
8310 * however this may change. */
8311 address_completion_end(compose->window);
8313 slist_free_strings(compose->to_list);
8314 g_slist_free(compose->to_list);
8315 slist_free_strings(compose->newsgroup_list);
8316 g_slist_free(compose->newsgroup_list);
8317 slist_free_strings(compose->header_list);
8318 g_slist_free(compose->header_list);
8320 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8322 g_hash_table_destroy(compose->email_hashtable);
8324 procmsg_msginfo_free(compose->targetinfo);
8325 procmsg_msginfo_free(compose->replyinfo);
8326 procmsg_msginfo_free(compose->fwdinfo);
8328 g_free(compose->replyto);
8329 g_free(compose->cc);
8330 g_free(compose->bcc);
8331 g_free(compose->newsgroups);
8332 g_free(compose->followup_to);
8334 g_free(compose->ml_post);
8336 g_free(compose->inreplyto);
8337 g_free(compose->references);
8338 g_free(compose->msgid);
8339 g_free(compose->boundary);
8341 g_free(compose->redirect_filename);
8342 if (compose->undostruct)
8343 undo_destroy(compose->undostruct);
8345 g_free(compose->sig_str);
8347 g_free(compose->exteditor_file);
8349 g_free(compose->orig_charset);
8351 g_free(compose->privacy_system);
8353 if (addressbook_get_target_compose() == compose)
8354 addressbook_set_target_compose(NULL);
8357 if (compose->gtkaspell) {
8358 gtkaspell_delete(compose->gtkaspell);
8359 compose->gtkaspell = NULL;
8363 if (!compose->batch) {
8364 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8365 prefs_common.compose_height = compose->window->allocation.height;
8368 if (!gtk_widget_get_parent(compose->paned))
8369 gtk_widget_destroy(compose->paned);
8370 gtk_widget_destroy(compose->popupmenu);
8372 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8373 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8374 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8376 gtk_widget_destroy(compose->window);
8377 toolbar_destroy(compose->toolbar);
8378 g_free(compose->toolbar);
8379 g_mutex_free(compose->mutex);
8383 static void compose_attach_info_free(AttachInfo *ainfo)
8385 g_free(ainfo->file);
8386 g_free(ainfo->content_type);
8387 g_free(ainfo->name);
8391 static void compose_attach_update_label(Compose *compose)
8396 GtkTreeModel *model;
8401 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8402 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8403 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8407 while(gtk_tree_model_iter_next(model, &iter))
8410 text = g_strdup_printf("(%d)", i);
8411 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8415 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8417 Compose *compose = (Compose *)data;
8418 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8419 GtkTreeSelection *selection;
8421 GtkTreeModel *model;
8423 selection = gtk_tree_view_get_selection(tree_view);
8424 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8429 for (cur = sel; cur != NULL; cur = cur->next) {
8430 GtkTreePath *path = cur->data;
8431 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8434 gtk_tree_path_free(path);
8437 for (cur = sel; cur != NULL; cur = cur->next) {
8438 GtkTreeRowReference *ref = cur->data;
8439 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8442 if (gtk_tree_model_get_iter(model, &iter, path))
8443 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8445 gtk_tree_path_free(path);
8446 gtk_tree_row_reference_free(ref);
8450 compose_attach_update_label(compose);
8453 static struct _AttachProperty
8456 GtkWidget *mimetype_entry;
8457 GtkWidget *encoding_optmenu;
8458 GtkWidget *path_entry;
8459 GtkWidget *filename_entry;
8461 GtkWidget *cancel_btn;
8464 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8466 gtk_tree_path_free((GtkTreePath *)ptr);
8469 static void compose_attach_property(GtkAction *action, gpointer data)
8471 Compose *compose = (Compose *)data;
8472 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8474 GtkComboBox *optmenu;
8475 GtkTreeSelection *selection;
8477 GtkTreeModel *model;
8480 static gboolean cancelled;
8482 /* only if one selected */
8483 selection = gtk_tree_view_get_selection(tree_view);
8484 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8487 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8491 path = (GtkTreePath *) sel->data;
8492 gtk_tree_model_get_iter(model, &iter, path);
8493 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8496 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8502 if (!attach_prop.window)
8503 compose_attach_property_create(&cancelled);
8504 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8505 gtk_widget_grab_focus(attach_prop.ok_btn);
8506 gtk_widget_show(attach_prop.window);
8507 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8509 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8510 if (ainfo->encoding == ENC_UNKNOWN)
8511 combobox_select_by_data(optmenu, ENC_BASE64);
8513 combobox_select_by_data(optmenu, ainfo->encoding);
8515 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8516 ainfo->content_type ? ainfo->content_type : "");
8517 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8518 ainfo->file ? ainfo->file : "");
8519 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8520 ainfo->name ? ainfo->name : "");
8523 const gchar *entry_text;
8525 gchar *cnttype = NULL;
8532 gtk_widget_hide(attach_prop.window);
8533 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8538 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8539 if (*entry_text != '\0') {
8542 text = g_strstrip(g_strdup(entry_text));
8543 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8544 cnttype = g_strdup(text);
8547 alertpanel_error(_("Invalid MIME type."));
8553 ainfo->encoding = combobox_get_active_data(optmenu);
8555 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8556 if (*entry_text != '\0') {
8557 if (is_file_exist(entry_text) &&
8558 (size = get_file_size(entry_text)) > 0)
8559 file = g_strdup(entry_text);
8562 (_("File doesn't exist or is empty."));
8568 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8569 if (*entry_text != '\0') {
8570 g_free(ainfo->name);
8571 ainfo->name = g_strdup(entry_text);
8575 g_free(ainfo->content_type);
8576 ainfo->content_type = cnttype;
8579 g_free(ainfo->file);
8583 ainfo->size = (goffset)size;
8585 /* update tree store */
8586 text = to_human_readable(ainfo->size);
8587 gtk_tree_model_get_iter(model, &iter, path);
8588 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8589 COL_MIMETYPE, ainfo->content_type,
8591 COL_NAME, ainfo->name,
8597 gtk_tree_path_free(path);
8600 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8602 label = gtk_label_new(str); \
8603 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8604 GTK_FILL, 0, 0, 0); \
8605 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8607 entry = gtk_entry_new(); \
8608 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8609 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8612 static void compose_attach_property_create(gboolean *cancelled)
8618 GtkWidget *mimetype_entry;
8621 GtkListStore *optmenu_menu;
8622 GtkWidget *path_entry;
8623 GtkWidget *filename_entry;
8626 GtkWidget *cancel_btn;
8627 GList *mime_type_list, *strlist;
8630 debug_print("Creating attach_property window...\n");
8632 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8633 gtk_widget_set_size_request(window, 480, -1);
8634 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8635 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8636 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8637 g_signal_connect(G_OBJECT(window), "delete_event",
8638 G_CALLBACK(attach_property_delete_event),
8640 g_signal_connect(G_OBJECT(window), "key_press_event",
8641 G_CALLBACK(attach_property_key_pressed),
8644 vbox = gtk_vbox_new(FALSE, 8);
8645 gtk_container_add(GTK_CONTAINER(window), vbox);
8647 table = gtk_table_new(4, 2, FALSE);
8648 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8649 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8650 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8652 label = gtk_label_new(_("MIME type"));
8653 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8655 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8656 mimetype_entry = gtk_combo_box_entry_new_text();
8657 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8658 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8660 /* stuff with list */
8661 mime_type_list = procmime_get_mime_type_list();
8663 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8664 MimeType *type = (MimeType *) mime_type_list->data;
8667 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8669 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8672 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8673 (GCompareFunc)strcmp2);
8676 for (mime_type_list = strlist; mime_type_list != NULL;
8677 mime_type_list = mime_type_list->next) {
8678 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8679 g_free(mime_type_list->data);
8681 g_list_free(strlist);
8682 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8683 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8685 label = gtk_label_new(_("Encoding"));
8686 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8688 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8690 hbox = gtk_hbox_new(FALSE, 0);
8691 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8692 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8694 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8695 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8697 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8698 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8699 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8700 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8701 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8703 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8705 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8706 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8708 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8709 &ok_btn, GTK_STOCK_OK,
8711 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8712 gtk_widget_grab_default(ok_btn);
8714 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8715 G_CALLBACK(attach_property_ok),
8717 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8718 G_CALLBACK(attach_property_cancel),
8721 gtk_widget_show_all(vbox);
8723 attach_prop.window = window;
8724 attach_prop.mimetype_entry = mimetype_entry;
8725 attach_prop.encoding_optmenu = optmenu;
8726 attach_prop.path_entry = path_entry;
8727 attach_prop.filename_entry = filename_entry;
8728 attach_prop.ok_btn = ok_btn;
8729 attach_prop.cancel_btn = cancel_btn;
8732 #undef SET_LABEL_AND_ENTRY
8734 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8740 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8746 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8747 gboolean *cancelled)
8755 static gboolean attach_property_key_pressed(GtkWidget *widget,
8757 gboolean *cancelled)
8759 if (event && event->keyval == GDK_Escape) {
8763 if (event && event->keyval == GDK_Return) {
8771 static void compose_exec_ext_editor(Compose *compose)
8778 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8779 G_DIR_SEPARATOR, compose);
8781 if (pipe(pipe_fds) < 0) {
8787 if ((pid = fork()) < 0) {
8794 /* close the write side of the pipe */
8797 compose->exteditor_file = g_strdup(tmp);
8798 compose->exteditor_pid = pid;
8800 compose_set_ext_editor_sensitive(compose, FALSE);
8803 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8805 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8807 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8811 } else { /* process-monitoring process */
8817 /* close the read side of the pipe */
8820 if (compose_write_body_to_file(compose, tmp) < 0) {
8821 fd_write_all(pipe_fds[1], "2\n", 2);
8825 pid_ed = compose_exec_ext_editor_real(tmp);
8827 fd_write_all(pipe_fds[1], "1\n", 2);
8831 /* wait until editor is terminated */
8832 waitpid(pid_ed, NULL, 0);
8834 fd_write_all(pipe_fds[1], "0\n", 2);
8841 #endif /* G_OS_UNIX */
8845 static gint compose_exec_ext_editor_real(const gchar *file)
8852 cm_return_val_if_fail(file != NULL, -1);
8854 if ((pid = fork()) < 0) {
8859 if (pid != 0) return pid;
8861 /* grandchild process */
8863 if (setpgid(0, getppid()))
8866 if (prefs_common_get_ext_editor_cmd() &&
8867 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8868 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8869 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8871 if (prefs_common_get_ext_editor_cmd())
8872 g_warning("External editor command-line is invalid: '%s'\n",
8873 prefs_common_get_ext_editor_cmd());
8874 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8877 cmdline = strsplit_with_quote(buf, " ", 1024);
8878 execvp(cmdline[0], cmdline);
8881 g_strfreev(cmdline);
8886 static gboolean compose_ext_editor_kill(Compose *compose)
8888 pid_t pgid = compose->exteditor_pid * -1;
8891 ret = kill(pgid, 0);
8893 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8897 msg = g_strdup_printf
8898 (_("The external editor is still working.\n"
8899 "Force terminating the process?\n"
8900 "process group id: %d"), -pgid);
8901 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8902 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8906 if (val == G_ALERTALTERNATE) {
8907 g_source_remove(compose->exteditor_tag);
8908 g_io_channel_shutdown(compose->exteditor_ch,
8910 g_io_channel_unref(compose->exteditor_ch);
8912 if (kill(pgid, SIGTERM) < 0) perror("kill");
8913 waitpid(compose->exteditor_pid, NULL, 0);
8915 g_warning("Terminated process group id: %d", -pgid);
8916 g_warning("Temporary file: %s",
8917 compose->exteditor_file);
8919 compose_set_ext_editor_sensitive(compose, TRUE);
8921 g_free(compose->exteditor_file);
8922 compose->exteditor_file = NULL;
8923 compose->exteditor_pid = -1;
8924 compose->exteditor_ch = NULL;
8925 compose->exteditor_tag = -1;
8933 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8937 Compose *compose = (Compose *)data;
8940 debug_print(_("Compose: input from monitoring process\n"));
8942 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8944 g_io_channel_shutdown(source, FALSE, NULL);
8945 g_io_channel_unref(source);
8947 waitpid(compose->exteditor_pid, NULL, 0);
8949 if (buf[0] == '0') { /* success */
8950 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8951 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8953 gtk_text_buffer_set_text(buffer, "", -1);
8954 compose_insert_file(compose, compose->exteditor_file);
8955 compose_changed_cb(NULL, compose);
8956 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8958 if (claws_unlink(compose->exteditor_file) < 0)
8959 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8960 } else if (buf[0] == '1') { /* failed */
8961 g_warning("Couldn't exec external editor\n");
8962 if (claws_unlink(compose->exteditor_file) < 0)
8963 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8964 } else if (buf[0] == '2') {
8965 g_warning("Couldn't write to file\n");
8966 } else if (buf[0] == '3') {
8967 g_warning("Pipe read failed\n");
8970 compose_set_ext_editor_sensitive(compose, TRUE);
8972 g_free(compose->exteditor_file);
8973 compose->exteditor_file = NULL;
8974 compose->exteditor_pid = -1;
8975 compose->exteditor_ch = NULL;
8976 compose->exteditor_tag = -1;
8981 static void compose_set_ext_editor_sensitive(Compose *compose,
8984 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8985 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8986 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8987 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8988 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8989 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8992 gtk_widget_set_sensitive(compose->text, sensitive);
8993 if (compose->toolbar->send_btn)
8994 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8995 if (compose->toolbar->sendl_btn)
8996 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8997 if (compose->toolbar->draft_btn)
8998 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8999 if (compose->toolbar->insert_btn)
9000 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9001 if (compose->toolbar->sig_btn)
9002 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9003 if (compose->toolbar->exteditor_btn)
9004 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9005 if (compose->toolbar->linewrap_current_btn)
9006 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9007 if (compose->toolbar->linewrap_all_btn)
9008 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9010 #endif /* G_OS_UNIX */
9013 * compose_undo_state_changed:
9015 * Change the sensivity of the menuentries undo and redo
9017 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9018 gint redo_state, gpointer data)
9020 Compose *compose = (Compose *)data;
9022 switch (undo_state) {
9023 case UNDO_STATE_TRUE:
9024 if (!undostruct->undo_state) {
9025 undostruct->undo_state = TRUE;
9026 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9029 case UNDO_STATE_FALSE:
9030 if (undostruct->undo_state) {
9031 undostruct->undo_state = FALSE;
9032 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9035 case UNDO_STATE_UNCHANGED:
9037 case UNDO_STATE_REFRESH:
9038 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9041 g_warning("Undo state not recognized");
9045 switch (redo_state) {
9046 case UNDO_STATE_TRUE:
9047 if (!undostruct->redo_state) {
9048 undostruct->redo_state = TRUE;
9049 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9052 case UNDO_STATE_FALSE:
9053 if (undostruct->redo_state) {
9054 undostruct->redo_state = FALSE;
9055 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9058 case UNDO_STATE_UNCHANGED:
9060 case UNDO_STATE_REFRESH:
9061 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9064 g_warning("Redo state not recognized");
9069 /* callback functions */
9071 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9072 * includes "non-client" (windows-izm) in calculation, so this calculation
9073 * may not be accurate.
9075 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9076 GtkAllocation *allocation,
9077 GtkSHRuler *shruler)
9079 if (prefs_common.show_ruler) {
9080 gint char_width = 0, char_height = 0;
9081 gint line_width_in_chars;
9083 gtkut_get_font_size(GTK_WIDGET(widget),
9084 &char_width, &char_height);
9085 line_width_in_chars =
9086 (allocation->width - allocation->x) / char_width;
9088 /* got the maximum */
9089 gtk_ruler_set_range(GTK_RULER(shruler),
9090 0.0, line_width_in_chars, 0,
9091 /*line_width_in_chars*/ char_width);
9100 ComposePrefType type;
9101 gboolean entry_marked;
9104 static void account_activated(GtkComboBox *optmenu, gpointer data)
9106 Compose *compose = (Compose *)data;
9109 gchar *folderidentifier;
9110 gint account_id = 0;
9113 GSList *list, *saved_list = NULL;
9114 HeaderEntryState *state;
9115 GtkRcStyle *style = NULL;
9116 static GdkColor yellow;
9117 static gboolean color_set = FALSE;
9119 /* Get ID of active account in the combo box */
9120 menu = gtk_combo_box_get_model(optmenu);
9121 gtk_combo_box_get_active_iter(optmenu, &iter);
9122 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9124 ac = account_find_from_id(account_id);
9125 cm_return_if_fail(ac != NULL);
9127 if (ac != compose->account) {
9128 compose_select_account(compose, ac, FALSE);
9130 for (list = compose->header_list; list; list = list->next) {
9131 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9133 if (hentry->type == PREF_ACCOUNT || !list->next) {
9134 compose_destroy_headerentry(compose, hentry);
9138 state = g_malloc0(sizeof(HeaderEntryState));
9139 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9140 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9141 state->entry = gtk_editable_get_chars(
9142 GTK_EDITABLE(hentry->entry), 0, -1);
9143 state->type = hentry->type;
9146 gdk_color_parse("#f5f6be", &yellow);
9147 color_set = gdk_colormap_alloc_color(
9148 gdk_colormap_get_system(),
9149 &yellow, FALSE, TRUE);
9152 style = gtk_widget_get_modifier_style(hentry->entry);
9153 state->entry_marked = gdk_color_equal(&yellow,
9154 &style->base[GTK_STATE_NORMAL]);
9156 saved_list = g_slist_append(saved_list, state);
9157 compose_destroy_headerentry(compose, hentry);
9160 compose->header_last = NULL;
9161 g_slist_free(compose->header_list);
9162 compose->header_list = NULL;
9163 compose->header_nextrow = 1;
9164 compose_create_header_entry(compose);
9166 if (ac->set_autocc && ac->auto_cc)
9167 compose_entry_append(compose, ac->auto_cc,
9168 COMPOSE_CC, PREF_ACCOUNT);
9170 if (ac->set_autobcc && ac->auto_bcc)
9171 compose_entry_append(compose, ac->auto_bcc,
9172 COMPOSE_BCC, PREF_ACCOUNT);
9174 if (ac->set_autoreplyto && ac->auto_replyto)
9175 compose_entry_append(compose, ac->auto_replyto,
9176 COMPOSE_REPLYTO, PREF_ACCOUNT);
9178 for (list = saved_list; list; list = list->next) {
9179 state = (HeaderEntryState *) list->data;
9181 compose_add_header_entry(compose, state->header,
9182 state->entry, state->type);
9183 if (state->entry_marked)
9184 compose_entry_mark_default_to(compose, state->entry);
9186 g_free(state->header);
9187 g_free(state->entry);
9190 g_slist_free(saved_list);
9192 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9193 (ac->protocol == A_NNTP) ?
9194 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9197 /* Set message save folder */
9198 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9201 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9202 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9204 compose_set_save_to(compose, NULL);
9205 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9206 folderidentifier = folder_item_get_identifier(account_get_special_folder
9207 (compose->account, F_OUTBOX));
9208 compose_set_save_to(compose, folderidentifier);
9209 g_free(folderidentifier);
9213 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9214 GtkTreeViewColumn *column, Compose *compose)
9216 compose_attach_property(NULL, compose);
9219 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9222 Compose *compose = (Compose *)data;
9223 GtkTreeSelection *attach_selection;
9224 gint attach_nr_selected;
9226 if (!event) return FALSE;
9228 if (event->button == 3) {
9229 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9230 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9232 if (attach_nr_selected > 0)
9234 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9235 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9237 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9238 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9241 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9242 NULL, NULL, event->button, event->time);
9249 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9252 Compose *compose = (Compose *)data;
9254 if (!event) return FALSE;
9256 switch (event->keyval) {
9258 compose_attach_remove_selected(NULL, compose);
9264 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9266 toolbar_comp_set_sensitive(compose, allow);
9267 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9268 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9270 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9272 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9273 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9276 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9280 static void compose_send_cb(GtkAction *action, gpointer data)
9282 Compose *compose = (Compose *)data;
9284 if (prefs_common.work_offline &&
9285 !inc_offline_should_override(TRUE,
9286 _("Claws Mail needs network access in order "
9287 "to send this email.")))
9290 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9291 g_source_remove(compose->draft_timeout_tag);
9292 compose->draft_timeout_tag = -1;
9295 compose_send(compose);
9298 static void compose_send_later_cb(GtkAction *action, gpointer data)
9300 Compose *compose = (Compose *)data;
9304 compose_allow_user_actions(compose, FALSE);
9305 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9306 compose_allow_user_actions(compose, TRUE);
9310 compose_close(compose);
9311 } else if (val == -1) {
9312 alertpanel_error(_("Could not queue message."));
9313 } else if (val == -2) {
9314 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9315 } else if (val == -3) {
9316 if (privacy_peek_error())
9317 alertpanel_error(_("Could not queue message for sending:\n\n"
9318 "Signature failed: %s"), privacy_get_error());
9319 } else if (val == -4) {
9320 alertpanel_error(_("Could not queue message for sending:\n\n"
9321 "Charset conversion failed."));
9322 } else if (val == -5) {
9323 alertpanel_error(_("Could not queue message for sending:\n\n"
9324 "Couldn't get recipient encryption key."));
9325 } else if (val == -6) {
9328 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9331 #define DRAFTED_AT_EXIT "drafted_at_exit"
9332 static void compose_register_draft(MsgInfo *info)
9334 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9335 DRAFTED_AT_EXIT, NULL);
9336 FILE *fp = g_fopen(filepath, "ab");
9339 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9347 gboolean compose_draft (gpointer data, guint action)
9349 Compose *compose = (Compose *)data;
9353 MsgFlags flag = {0, 0};
9354 static gboolean lock = FALSE;
9355 MsgInfo *newmsginfo;
9357 gboolean target_locked = FALSE;
9358 gboolean err = FALSE;
9360 if (lock) return FALSE;
9362 if (compose->sending)
9365 draft = account_get_special_folder(compose->account, F_DRAFT);
9366 cm_return_val_if_fail(draft != NULL, FALSE);
9368 if (!g_mutex_trylock(compose->mutex)) {
9369 /* we don't want to lock the mutex once it's available,
9370 * because as the only other part of compose.c locking
9371 * it is compose_close - which means once unlocked,
9372 * the compose struct will be freed */
9373 debug_print("couldn't lock mutex, probably sending\n");
9379 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9380 G_DIR_SEPARATOR, compose);
9381 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9382 FILE_OP_ERROR(tmp, "fopen");
9386 /* chmod for security */
9387 if (change_file_mode_rw(fp, tmp) < 0) {
9388 FILE_OP_ERROR(tmp, "chmod");
9389 g_warning("can't change file mode\n");
9392 /* Save draft infos */
9393 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9394 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9396 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9397 gchar *savefolderid;
9399 savefolderid = compose_get_save_to(compose);
9400 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9401 g_free(savefolderid);
9403 if (compose->return_receipt) {
9404 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9406 if (compose->privacy_system) {
9407 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9408 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9409 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9412 /* Message-ID of message replying to */
9413 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9416 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9417 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9420 /* Message-ID of message forwarding to */
9421 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9424 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9425 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9429 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9430 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9432 /* end of headers */
9433 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9440 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9444 if (fclose(fp) == EOF) {
9448 if (compose->targetinfo) {
9449 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9450 flag.perm_flags = target_locked?MSG_LOCKED:0;
9452 flag.tmp_flags = MSG_DRAFT;
9454 folder_item_scan(draft);
9455 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9456 MsgInfo *tmpinfo = NULL;
9457 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9458 if (compose->msgid) {
9459 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9462 msgnum = tmpinfo->msgnum;
9463 procmsg_msginfo_free(tmpinfo);
9464 debug_print("got draft msgnum %d from scanning\n", msgnum);
9466 debug_print("didn't get draft msgnum after scanning\n");
9469 debug_print("got draft msgnum %d from adding\n", msgnum);
9475 if (action != COMPOSE_AUTO_SAVE) {
9476 if (action != COMPOSE_DRAFT_FOR_EXIT)
9477 alertpanel_error(_("Could not save draft."));
9480 gtkut_window_popup(compose->window);
9481 val = alertpanel_full(_("Could not save draft"),
9482 _("Could not save draft.\n"
9483 "Do you want to cancel exit or discard this email?"),
9484 _("_Cancel exit"), _("_Discard email"), NULL,
9485 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9486 if (val == G_ALERTALTERNATE) {
9488 g_mutex_unlock(compose->mutex); /* must be done before closing */
9489 compose_close(compose);
9493 g_mutex_unlock(compose->mutex); /* must be done before closing */
9502 if (compose->mode == COMPOSE_REEDIT) {
9503 compose_remove_reedit_target(compose, TRUE);
9506 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9509 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9511 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9513 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9514 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9515 procmsg_msginfo_set_flags(newmsginfo, 0,
9516 MSG_HAS_ATTACHMENT);
9518 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9519 compose_register_draft(newmsginfo);
9521 procmsg_msginfo_free(newmsginfo);
9524 folder_item_scan(draft);
9526 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9528 g_mutex_unlock(compose->mutex); /* must be done before closing */
9529 compose_close(compose);
9535 path = folder_item_fetch_msg(draft, msgnum);
9537 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9540 if (g_stat(path, &s) < 0) {
9541 FILE_OP_ERROR(path, "stat");
9547 procmsg_msginfo_free(compose->targetinfo);
9548 compose->targetinfo = procmsg_msginfo_new();
9549 compose->targetinfo->msgnum = msgnum;
9550 compose->targetinfo->size = (goffset)s.st_size;
9551 compose->targetinfo->mtime = s.st_mtime;
9552 compose->targetinfo->folder = draft;
9554 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9555 compose->mode = COMPOSE_REEDIT;
9557 if (action == COMPOSE_AUTO_SAVE) {
9558 compose->autosaved_draft = compose->targetinfo;
9560 compose->modified = FALSE;
9561 compose_set_title(compose);
9565 g_mutex_unlock(compose->mutex);
9569 void compose_clear_exit_drafts(void)
9571 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9572 DRAFTED_AT_EXIT, NULL);
9573 if (is_file_exist(filepath))
9574 claws_unlink(filepath);
9579 void compose_reopen_exit_drafts(void)
9581 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9582 DRAFTED_AT_EXIT, NULL);
9583 FILE *fp = g_fopen(filepath, "rb");
9587 while (fgets(buf, sizeof(buf), fp)) {
9588 gchar **parts = g_strsplit(buf, "\t", 2);
9589 const gchar *folder = parts[0];
9590 int msgnum = parts[1] ? atoi(parts[1]):-1;
9592 if (folder && *folder && msgnum > -1) {
9593 FolderItem *item = folder_find_item_from_identifier(folder);
9594 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9596 compose_reedit(info, FALSE);
9603 compose_clear_exit_drafts();
9606 static void compose_save_cb(GtkAction *action, gpointer data)
9608 Compose *compose = (Compose *)data;
9609 compose_draft(compose, COMPOSE_KEEP_EDITING);
9610 compose->rmode = COMPOSE_REEDIT;
9613 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9615 if (compose && file_list) {
9618 for ( tmp = file_list; tmp; tmp = tmp->next) {
9619 gchar *file = (gchar *) tmp->data;
9620 gchar *utf8_filename = conv_filename_to_utf8(file);
9621 compose_attach_append(compose, file, utf8_filename, NULL);
9622 compose_changed_cb(NULL, compose);
9627 g_free(utf8_filename);
9632 static void compose_attach_cb(GtkAction *action, gpointer data)
9634 Compose *compose = (Compose *)data;
9637 if (compose->redirect_filename != NULL)
9640 file_list = filesel_select_multiple_files_open(_("Select file"));
9643 compose_attach_from_list(compose, file_list, TRUE);
9644 g_list_free(file_list);
9648 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9650 Compose *compose = (Compose *)data;
9652 gint files_inserted = 0;
9654 file_list = filesel_select_multiple_files_open(_("Select file"));
9659 for ( tmp = file_list; tmp; tmp = tmp->next) {
9660 gchar *file = (gchar *) tmp->data;
9661 gchar *filedup = g_strdup(file);
9662 gchar *shortfile = g_path_get_basename(filedup);
9663 ComposeInsertResult res;
9664 /* insert the file if the file is short or if the user confirmed that
9665 he/she wants to insert the large file */
9666 res = compose_insert_file(compose, file);
9667 if (res == COMPOSE_INSERT_READ_ERROR) {
9668 alertpanel_error(_("File '%s' could not be read."), shortfile);
9669 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9670 alertpanel_error(_("File '%s' contained invalid characters\n"
9671 "for the current encoding, insertion may be incorrect."),
9673 } else if (res == COMPOSE_INSERT_SUCCESS)
9680 g_list_free(file_list);
9684 if (files_inserted > 0 && compose->gtkaspell &&
9685 compose->gtkaspell->check_while_typing)
9686 gtkaspell_highlight_all(compose->gtkaspell);
9690 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9692 Compose *compose = (Compose *)data;
9694 compose_insert_sig(compose, FALSE);
9697 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9701 Compose *compose = (Compose *)data;
9703 gtkut_widget_get_uposition(widget, &x, &y);
9704 if (!compose->batch) {
9705 prefs_common.compose_x = x;
9706 prefs_common.compose_y = y;
9708 if (compose->sending || compose->updating)
9710 compose_close_cb(NULL, compose);
9714 void compose_close_toolbar(Compose *compose)
9716 compose_close_cb(NULL, compose);
9719 static void compose_close_cb(GtkAction *action, gpointer data)
9721 Compose *compose = (Compose *)data;
9725 if (compose->exteditor_tag != -1) {
9726 if (!compose_ext_editor_kill(compose))
9731 if (compose->modified) {
9732 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9733 if (!g_mutex_trylock(compose->mutex)) {
9734 /* we don't want to lock the mutex once it's available,
9735 * because as the only other part of compose.c locking
9736 * it is compose_close - which means once unlocked,
9737 * the compose struct will be freed */
9738 debug_print("couldn't lock mutex, probably sending\n");
9742 val = alertpanel(_("Discard message"),
9743 _("This message has been modified. Discard it?"),
9744 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9746 val = alertpanel(_("Save changes"),
9747 _("This message has been modified. Save the latest changes?"),
9748 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9750 g_mutex_unlock(compose->mutex);
9752 case G_ALERTDEFAULT:
9753 if (prefs_common.autosave && !reedit)
9754 compose_remove_draft(compose);
9756 case G_ALERTALTERNATE:
9757 compose_draft(data, COMPOSE_QUIT_EDITING);
9764 compose_close(compose);
9767 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9769 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9770 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9771 Compose *compose = (Compose *) data;
9774 compose->out_encoding = (CharSet)value;
9777 static void compose_address_cb(GtkAction *action, gpointer data)
9779 Compose *compose = (Compose *)data;
9781 addressbook_open(compose);
9784 static void about_show_cb(GtkAction *action, gpointer data)
9789 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9791 Compose *compose = (Compose *)data;
9796 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9797 cm_return_if_fail(tmpl != NULL);
9799 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9801 val = alertpanel(_("Apply template"), msg,
9802 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9805 if (val == G_ALERTDEFAULT)
9806 compose_template_apply(compose, tmpl, TRUE);
9807 else if (val == G_ALERTALTERNATE)
9808 compose_template_apply(compose, tmpl, FALSE);
9811 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9813 Compose *compose = (Compose *)data;
9815 compose_exec_ext_editor(compose);
9818 static void compose_undo_cb(GtkAction *action, gpointer data)
9820 Compose *compose = (Compose *)data;
9821 gboolean prev_autowrap = compose->autowrap;
9823 compose->autowrap = FALSE;
9824 undo_undo(compose->undostruct);
9825 compose->autowrap = prev_autowrap;
9828 static void compose_redo_cb(GtkAction *action, gpointer data)
9830 Compose *compose = (Compose *)data;
9831 gboolean prev_autowrap = compose->autowrap;
9833 compose->autowrap = FALSE;
9834 undo_redo(compose->undostruct);
9835 compose->autowrap = prev_autowrap;
9838 static void entry_cut_clipboard(GtkWidget *entry)
9840 if (GTK_IS_EDITABLE(entry))
9841 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9842 else if (GTK_IS_TEXT_VIEW(entry))
9843 gtk_text_buffer_cut_clipboard(
9844 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9845 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9849 static void entry_copy_clipboard(GtkWidget *entry)
9851 if (GTK_IS_EDITABLE(entry))
9852 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9853 else if (GTK_IS_TEXT_VIEW(entry))
9854 gtk_text_buffer_copy_clipboard(
9855 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9856 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9859 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9860 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9862 if (GTK_IS_TEXT_VIEW(entry)) {
9863 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9864 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9865 GtkTextIter start_iter, end_iter;
9867 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9869 if (contents == NULL)
9872 /* we shouldn't delete the selection when middle-click-pasting, or we
9873 * can't mid-click-paste our own selection */
9874 if (clip != GDK_SELECTION_PRIMARY) {
9875 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9876 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9879 if (insert_place == NULL) {
9880 /* if insert_place isn't specified, insert at the cursor.
9881 * used for Ctrl-V pasting */
9882 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9883 start = gtk_text_iter_get_offset(&start_iter);
9884 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9886 /* if insert_place is specified, paste here.
9887 * used for mid-click-pasting */
9888 start = gtk_text_iter_get_offset(insert_place);
9889 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9890 if (prefs_common.primary_paste_unselects)
9891 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9895 /* paste unwrapped: mark the paste so it's not wrapped later */
9896 end = start + strlen(contents);
9897 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9898 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9899 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9900 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9901 /* rewrap paragraph now (after a mid-click-paste) */
9902 mark_start = gtk_text_buffer_get_insert(buffer);
9903 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9904 gtk_text_iter_backward_char(&start_iter);
9905 compose_beautify_paragraph(compose, &start_iter, TRUE);
9907 } else if (GTK_IS_EDITABLE(entry))
9908 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9910 compose->modified = TRUE;
9913 static void entry_allsel(GtkWidget *entry)
9915 if (GTK_IS_EDITABLE(entry))
9916 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9917 else if (GTK_IS_TEXT_VIEW(entry)) {
9918 GtkTextIter startiter, enditer;
9919 GtkTextBuffer *textbuf;
9921 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9922 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9923 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9925 gtk_text_buffer_move_mark_by_name(textbuf,
9926 "selection_bound", &startiter);
9927 gtk_text_buffer_move_mark_by_name(textbuf,
9928 "insert", &enditer);
9932 static void compose_cut_cb(GtkAction *action, gpointer data)
9934 Compose *compose = (Compose *)data;
9935 if (compose->focused_editable
9936 #ifndef GENERIC_UMPC
9937 && gtkut_widget_has_focus(compose->focused_editable)
9940 entry_cut_clipboard(compose->focused_editable);
9943 static void compose_copy_cb(GtkAction *action, gpointer data)
9945 Compose *compose = (Compose *)data;
9946 if (compose->focused_editable
9947 #ifndef GENERIC_UMPC
9948 && gtkut_widget_has_focus(compose->focused_editable)
9951 entry_copy_clipboard(compose->focused_editable);
9954 static void compose_paste_cb(GtkAction *action, gpointer data)
9956 Compose *compose = (Compose *)data;
9958 GtkTextBuffer *buffer;
9960 if (compose->focused_editable &&
9961 gtkut_widget_has_focus(compose->focused_editable))
9962 entry_paste_clipboard(compose, compose->focused_editable,
9963 prefs_common.linewrap_pastes,
9964 GDK_SELECTION_CLIPBOARD, NULL);
9968 if (gtkut_widget_has_focus(compose->text) &&
9969 compose->gtkaspell &&
9970 compose->gtkaspell->check_while_typing)
9971 gtkaspell_highlight_all(compose->gtkaspell);
9975 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9977 Compose *compose = (Compose *)data;
9978 gint wrap_quote = prefs_common.linewrap_quote;
9979 if (compose->focused_editable
9980 #ifndef GENERIC_UMPC
9981 && gtkut_widget_has_focus(compose->focused_editable)
9984 /* let text_insert() (called directly or at a later time
9985 * after the gtk_editable_paste_clipboard) know that
9986 * text is to be inserted as a quotation. implemented
9987 * by using a simple refcount... */
9988 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9989 G_OBJECT(compose->focused_editable),
9990 "paste_as_quotation"));
9991 g_object_set_data(G_OBJECT(compose->focused_editable),
9992 "paste_as_quotation",
9993 GINT_TO_POINTER(paste_as_quotation + 1));
9994 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9995 entry_paste_clipboard(compose, compose->focused_editable,
9996 prefs_common.linewrap_pastes,
9997 GDK_SELECTION_CLIPBOARD, NULL);
9998 prefs_common.linewrap_quote = wrap_quote;
10002 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10004 Compose *compose = (Compose *)data;
10005 gint prev_autowrap;
10006 GtkTextBuffer *buffer;
10008 if (compose->focused_editable
10009 #ifndef GENERIC_UMPC
10010 && gtkut_widget_has_focus(compose->focused_editable)
10013 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10014 GDK_SELECTION_CLIPBOARD, NULL);
10018 if (gtkut_widget_has_focus(compose->text) &&
10019 compose->gtkaspell &&
10020 compose->gtkaspell->check_while_typing)
10021 gtkaspell_highlight_all(compose->gtkaspell);
10025 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10027 Compose *compose = (Compose *)data;
10028 gint prev_autowrap;
10029 GtkTextBuffer *buffer;
10031 if (compose->focused_editable
10032 #ifndef GENERIC_UMPC
10033 && gtkut_widget_has_focus(compose->focused_editable)
10036 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10037 GDK_SELECTION_CLIPBOARD, NULL);
10041 if (gtkut_widget_has_focus(compose->text) &&
10042 compose->gtkaspell &&
10043 compose->gtkaspell->check_while_typing)
10044 gtkaspell_highlight_all(compose->gtkaspell);
10048 static void compose_allsel_cb(GtkAction *action, gpointer data)
10050 Compose *compose = (Compose *)data;
10051 if (compose->focused_editable
10052 #ifndef GENERIC_UMPC
10053 && gtkut_widget_has_focus(compose->focused_editable)
10056 entry_allsel(compose->focused_editable);
10059 static void textview_move_beginning_of_line (GtkTextView *text)
10061 GtkTextBuffer *buffer;
10065 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10067 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10068 mark = gtk_text_buffer_get_insert(buffer);
10069 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10070 gtk_text_iter_set_line_offset(&ins, 0);
10071 gtk_text_buffer_place_cursor(buffer, &ins);
10074 static void textview_move_forward_character (GtkTextView *text)
10076 GtkTextBuffer *buffer;
10080 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10082 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10083 mark = gtk_text_buffer_get_insert(buffer);
10084 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10085 if (gtk_text_iter_forward_cursor_position(&ins))
10086 gtk_text_buffer_place_cursor(buffer, &ins);
10089 static void textview_move_backward_character (GtkTextView *text)
10091 GtkTextBuffer *buffer;
10095 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10097 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10098 mark = gtk_text_buffer_get_insert(buffer);
10099 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10100 if (gtk_text_iter_backward_cursor_position(&ins))
10101 gtk_text_buffer_place_cursor(buffer, &ins);
10104 static void textview_move_forward_word (GtkTextView *text)
10106 GtkTextBuffer *buffer;
10111 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10113 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10114 mark = gtk_text_buffer_get_insert(buffer);
10115 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10116 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10117 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10118 gtk_text_iter_backward_word_start(&ins);
10119 gtk_text_buffer_place_cursor(buffer, &ins);
10123 static void textview_move_backward_word (GtkTextView *text)
10125 GtkTextBuffer *buffer;
10130 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10132 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10133 mark = gtk_text_buffer_get_insert(buffer);
10134 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10135 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10136 if (gtk_text_iter_backward_word_starts(&ins, 1))
10137 gtk_text_buffer_place_cursor(buffer, &ins);
10140 static void textview_move_end_of_line (GtkTextView *text)
10142 GtkTextBuffer *buffer;
10146 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10148 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10149 mark = gtk_text_buffer_get_insert(buffer);
10150 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10151 if (gtk_text_iter_forward_to_line_end(&ins))
10152 gtk_text_buffer_place_cursor(buffer, &ins);
10155 static void textview_move_next_line (GtkTextView *text)
10157 GtkTextBuffer *buffer;
10162 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10164 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10165 mark = gtk_text_buffer_get_insert(buffer);
10166 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10167 offset = gtk_text_iter_get_line_offset(&ins);
10168 if (gtk_text_iter_forward_line(&ins)) {
10169 gtk_text_iter_set_line_offset(&ins, offset);
10170 gtk_text_buffer_place_cursor(buffer, &ins);
10174 static void textview_move_previous_line (GtkTextView *text)
10176 GtkTextBuffer *buffer;
10181 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10183 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10184 mark = gtk_text_buffer_get_insert(buffer);
10185 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10186 offset = gtk_text_iter_get_line_offset(&ins);
10187 if (gtk_text_iter_backward_line(&ins)) {
10188 gtk_text_iter_set_line_offset(&ins, offset);
10189 gtk_text_buffer_place_cursor(buffer, &ins);
10193 static void textview_delete_forward_character (GtkTextView *text)
10195 GtkTextBuffer *buffer;
10197 GtkTextIter ins, end_iter;
10199 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10201 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10202 mark = gtk_text_buffer_get_insert(buffer);
10203 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10205 if (gtk_text_iter_forward_char(&end_iter)) {
10206 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10210 static void textview_delete_backward_character (GtkTextView *text)
10212 GtkTextBuffer *buffer;
10214 GtkTextIter ins, end_iter;
10216 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10218 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10219 mark = gtk_text_buffer_get_insert(buffer);
10220 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10222 if (gtk_text_iter_backward_char(&end_iter)) {
10223 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10227 static void textview_delete_forward_word (GtkTextView *text)
10229 GtkTextBuffer *buffer;
10231 GtkTextIter ins, end_iter;
10233 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10235 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10236 mark = gtk_text_buffer_get_insert(buffer);
10237 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10239 if (gtk_text_iter_forward_word_end(&end_iter)) {
10240 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10244 static void textview_delete_backward_word (GtkTextView *text)
10246 GtkTextBuffer *buffer;
10248 GtkTextIter ins, end_iter;
10250 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10252 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10253 mark = gtk_text_buffer_get_insert(buffer);
10254 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10256 if (gtk_text_iter_backward_word_start(&end_iter)) {
10257 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10261 static void textview_delete_line (GtkTextView *text)
10263 GtkTextBuffer *buffer;
10265 GtkTextIter ins, start_iter, end_iter;
10267 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10269 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10270 mark = gtk_text_buffer_get_insert(buffer);
10271 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10274 gtk_text_iter_set_line_offset(&start_iter, 0);
10277 if (gtk_text_iter_ends_line(&end_iter)){
10278 if (!gtk_text_iter_forward_char(&end_iter))
10279 gtk_text_iter_backward_char(&start_iter);
10282 gtk_text_iter_forward_to_line_end(&end_iter);
10283 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10286 static void textview_delete_to_line_end (GtkTextView *text)
10288 GtkTextBuffer *buffer;
10290 GtkTextIter ins, end_iter;
10292 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10294 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10295 mark = gtk_text_buffer_get_insert(buffer);
10296 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10298 if (gtk_text_iter_ends_line(&end_iter))
10299 gtk_text_iter_forward_char(&end_iter);
10301 gtk_text_iter_forward_to_line_end(&end_iter);
10302 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10305 #define DO_ACTION(name, act) { \
10306 if(!strcmp(name, a_name)) { \
10310 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10312 const gchar *a_name = gtk_action_get_name(action);
10313 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10314 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10315 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10316 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10317 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10318 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10319 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10320 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10321 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10322 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10323 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10324 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10325 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10326 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10330 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10332 Compose *compose = (Compose *)data;
10333 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10334 ComposeCallAdvancedAction action = -1;
10336 action = compose_call_advanced_action_from_path(gaction);
10339 void (*do_action) (GtkTextView *text);
10340 } action_table[] = {
10341 {textview_move_beginning_of_line},
10342 {textview_move_forward_character},
10343 {textview_move_backward_character},
10344 {textview_move_forward_word},
10345 {textview_move_backward_word},
10346 {textview_move_end_of_line},
10347 {textview_move_next_line},
10348 {textview_move_previous_line},
10349 {textview_delete_forward_character},
10350 {textview_delete_backward_character},
10351 {textview_delete_forward_word},
10352 {textview_delete_backward_word},
10353 {textview_delete_line},
10354 {textview_delete_to_line_end}
10357 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10359 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10360 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10361 if (action_table[action].do_action)
10362 action_table[action].do_action(text);
10364 g_warning("Not implemented yet.");
10368 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10372 if (GTK_IS_EDITABLE(widget)) {
10373 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10374 gtk_editable_set_position(GTK_EDITABLE(widget),
10377 if (widget->parent && widget->parent->parent
10378 && widget->parent->parent->parent) {
10379 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10380 gint y = widget->allocation.y;
10381 gint height = widget->allocation.height;
10382 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10383 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10385 if (y < (int)shown->value) {
10386 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10388 if (y + height > (int)shown->value + (int)shown->page_size) {
10389 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10390 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10391 y + height - (int)shown->page_size - 1);
10393 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10394 (int)shown->upper - (int)shown->page_size - 1);
10401 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10402 compose->focused_editable = widget;
10404 #ifdef GENERIC_UMPC
10405 if (GTK_IS_TEXT_VIEW(widget)
10406 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10407 g_object_ref(compose->notebook);
10408 g_object_ref(compose->edit_vbox);
10409 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10410 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10411 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10412 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10413 g_object_unref(compose->notebook);
10414 g_object_unref(compose->edit_vbox);
10415 g_signal_handlers_block_by_func(G_OBJECT(widget),
10416 G_CALLBACK(compose_grab_focus_cb),
10418 gtk_widget_grab_focus(widget);
10419 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10420 G_CALLBACK(compose_grab_focus_cb),
10422 } else if (!GTK_IS_TEXT_VIEW(widget)
10423 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10424 g_object_ref(compose->notebook);
10425 g_object_ref(compose->edit_vbox);
10426 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10427 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10428 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10429 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10430 g_object_unref(compose->notebook);
10431 g_object_unref(compose->edit_vbox);
10432 g_signal_handlers_block_by_func(G_OBJECT(widget),
10433 G_CALLBACK(compose_grab_focus_cb),
10435 gtk_widget_grab_focus(widget);
10436 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10437 G_CALLBACK(compose_grab_focus_cb),
10443 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10445 compose->modified = TRUE;
10446 // compose_beautify_paragraph(compose, NULL, TRUE);
10447 #ifndef GENERIC_UMPC
10448 compose_set_title(compose);
10452 static void compose_wrap_cb(GtkAction *action, gpointer data)
10454 Compose *compose = (Compose *)data;
10455 compose_beautify_paragraph(compose, NULL, TRUE);
10458 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10460 Compose *compose = (Compose *)data;
10461 compose_wrap_all_full(compose, TRUE);
10464 static void compose_find_cb(GtkAction *action, gpointer data)
10466 Compose *compose = (Compose *)data;
10468 message_search_compose(compose);
10471 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10474 Compose *compose = (Compose *)data;
10475 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10476 if (compose->autowrap)
10477 compose_wrap_all_full(compose, TRUE);
10478 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10481 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10484 Compose *compose = (Compose *)data;
10485 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10488 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10490 Compose *compose = (Compose *)data;
10492 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10495 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10497 Compose *compose = (Compose *)data;
10499 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10502 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10504 g_free(compose->privacy_system);
10506 compose->privacy_system = g_strdup(account->default_privacy_system);
10507 compose_update_privacy_system_menu_item(compose, warn);
10510 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10512 Compose *compose = (Compose *)data;
10514 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10515 gtk_widget_show(compose->ruler_hbox);
10516 prefs_common.show_ruler = TRUE;
10518 gtk_widget_hide(compose->ruler_hbox);
10519 gtk_widget_queue_resize(compose->edit_vbox);
10520 prefs_common.show_ruler = FALSE;
10524 static void compose_attach_drag_received_cb (GtkWidget *widget,
10525 GdkDragContext *context,
10528 GtkSelectionData *data,
10531 gpointer user_data)
10533 Compose *compose = (Compose *)user_data;
10536 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10538 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10540 ) && gtk_drag_get_source_widget(context) !=
10541 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10542 list = uri_list_extract_filenames((const gchar *)data->data);
10543 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10544 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10545 compose_attach_append
10546 (compose, (const gchar *)tmp->data,
10547 utf8_filename, NULL);
10548 g_free(utf8_filename);
10550 if (list) compose_changed_cb(NULL, compose);
10551 list_free_strings(list);
10553 } else if (gtk_drag_get_source_widget(context)
10554 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10555 /* comes from our summaryview */
10556 SummaryView * summaryview = NULL;
10557 GSList * list = NULL, *cur = NULL;
10559 if (mainwindow_get_mainwindow())
10560 summaryview = mainwindow_get_mainwindow()->summaryview;
10563 list = summary_get_selected_msg_list(summaryview);
10565 for (cur = list; cur; cur = cur->next) {
10566 MsgInfo *msginfo = (MsgInfo *)cur->data;
10567 gchar *file = NULL;
10569 file = procmsg_get_message_file_full(msginfo,
10572 compose_attach_append(compose, (const gchar *)file,
10573 (const gchar *)file, "message/rfc822");
10577 g_slist_free(list);
10581 static gboolean compose_drag_drop(GtkWidget *widget,
10582 GdkDragContext *drag_context,
10584 guint time, gpointer user_data)
10586 /* not handling this signal makes compose_insert_drag_received_cb
10591 static void compose_insert_drag_received_cb (GtkWidget *widget,
10592 GdkDragContext *drag_context,
10595 GtkSelectionData *data,
10598 gpointer user_data)
10600 Compose *compose = (Compose *)user_data;
10603 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10606 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10608 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10610 AlertValue val = G_ALERTDEFAULT;
10612 list = uri_list_extract_filenames((const gchar *)data->data);
10613 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10614 /* Assume a list of no files, and data has ://, is a remote link */
10615 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10616 gchar *tmpfile = get_tmp_file();
10617 str_write_to_file(tmpdata, tmpfile);
10619 compose_insert_file(compose, tmpfile);
10620 claws_unlink(tmpfile);
10622 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10623 compose_beautify_paragraph(compose, NULL, TRUE);
10626 switch (prefs_common.compose_dnd_mode) {
10627 case COMPOSE_DND_ASK:
10628 val = alertpanel_full(_("Insert or attach?"),
10629 _("Do you want to insert the contents of the file(s) "
10630 "into the message body, or attach it to the email?"),
10631 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10632 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10634 case COMPOSE_DND_INSERT:
10635 val = G_ALERTALTERNATE;
10637 case COMPOSE_DND_ATTACH:
10638 val = G_ALERTOTHER;
10641 /* unexpected case */
10642 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10645 if (val & G_ALERTDISABLE) {
10646 val &= ~G_ALERTDISABLE;
10647 /* remember what action to perform by default, only if we don't click Cancel */
10648 if (val == G_ALERTALTERNATE)
10649 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10650 else if (val == G_ALERTOTHER)
10651 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10654 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10655 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10656 list_free_strings(list);
10659 } else if (val == G_ALERTOTHER) {
10660 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10661 list_free_strings(list);
10666 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10667 compose_insert_file(compose, (const gchar *)tmp->data);
10669 list_free_strings(list);
10671 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10676 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10679 static void compose_header_drag_received_cb (GtkWidget *widget,
10680 GdkDragContext *drag_context,
10683 GtkSelectionData *data,
10686 gpointer user_data)
10688 GtkEditable *entry = (GtkEditable *)user_data;
10689 gchar *email = (gchar *)data->data;
10691 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10694 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10695 gchar *decoded=g_new(gchar, strlen(email));
10698 email += strlen("mailto:");
10699 decode_uri(decoded, email); /* will fit */
10700 gtk_editable_delete_text(entry, 0, -1);
10701 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10702 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10706 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10709 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10711 Compose *compose = (Compose *)data;
10713 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10714 compose->return_receipt = TRUE;
10716 compose->return_receipt = FALSE;
10719 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10721 Compose *compose = (Compose *)data;
10723 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10724 compose->remove_references = TRUE;
10726 compose->remove_references = FALSE;
10729 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10730 ComposeHeaderEntry *headerentry)
10732 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10736 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10737 GdkEventKey *event,
10738 ComposeHeaderEntry *headerentry)
10740 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10741 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10742 !(event->state & GDK_MODIFIER_MASK) &&
10743 (event->keyval == GDK_BackSpace) &&
10744 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10745 gtk_container_remove
10746 (GTK_CONTAINER(headerentry->compose->header_table),
10747 headerentry->combo);
10748 gtk_container_remove
10749 (GTK_CONTAINER(headerentry->compose->header_table),
10750 headerentry->entry);
10751 headerentry->compose->header_list =
10752 g_slist_remove(headerentry->compose->header_list,
10754 g_free(headerentry);
10755 } else if (event->keyval == GDK_Tab) {
10756 if (headerentry->compose->header_last == headerentry) {
10757 /* Override default next focus, and give it to subject_entry
10758 * instead of notebook tabs
10760 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10761 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10768 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10769 ComposeHeaderEntry *headerentry)
10771 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10772 compose_create_header_entry(headerentry->compose);
10773 g_signal_handlers_disconnect_matched
10774 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10775 0, 0, NULL, NULL, headerentry);
10777 /* Automatically scroll down */
10778 GTK_EVENTS_FLUSH();
10779 compose_show_first_last_header(headerentry->compose, FALSE);
10785 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10787 GtkAdjustment *vadj;
10789 cm_return_if_fail(compose);
10790 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10791 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10792 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10793 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10794 gtk_adjustment_changed(vadj);
10797 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10798 const gchar *text, gint len, Compose *compose)
10800 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10801 (G_OBJECT(compose->text), "paste_as_quotation"));
10804 cm_return_if_fail(text != NULL);
10806 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10807 G_CALLBACK(text_inserted),
10809 if (paste_as_quotation) {
10811 const gchar *qmark;
10813 GtkTextIter start_iter;
10816 len = strlen(text);
10818 new_text = g_strndup(text, len);
10820 qmark = compose_quote_char_from_context(compose);
10822 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10823 gtk_text_buffer_place_cursor(buffer, iter);
10825 pos = gtk_text_iter_get_offset(iter);
10827 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10828 _("Quote format error at line %d."));
10829 quote_fmt_reset_vartable();
10831 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10832 GINT_TO_POINTER(paste_as_quotation - 1));
10834 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10835 gtk_text_buffer_place_cursor(buffer, iter);
10836 gtk_text_buffer_delete_mark(buffer, mark);
10838 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10839 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10840 compose_beautify_paragraph(compose, &start_iter, FALSE);
10841 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10842 gtk_text_buffer_delete_mark(buffer, mark);
10844 if (strcmp(text, "\n") || compose->automatic_break
10845 || gtk_text_iter_starts_line(iter)) {
10846 GtkTextIter before_ins;
10847 gtk_text_buffer_insert(buffer, iter, text, len);
10848 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10849 before_ins = *iter;
10850 gtk_text_iter_backward_chars(&before_ins, len);
10851 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10854 /* check if the preceding is just whitespace or quote */
10855 GtkTextIter start_line;
10856 gchar *tmp = NULL, *quote = NULL;
10857 gint quote_len = 0, is_normal = 0;
10858 start_line = *iter;
10859 gtk_text_iter_set_line_offset(&start_line, 0);
10860 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10863 if (*tmp == '\0') {
10866 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10874 gtk_text_buffer_insert(buffer, iter, text, len);
10876 gtk_text_buffer_insert_with_tags_by_name(buffer,
10877 iter, text, len, "no_join", NULL);
10882 if (!paste_as_quotation) {
10883 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10884 compose_beautify_paragraph(compose, iter, FALSE);
10885 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10886 gtk_text_buffer_delete_mark(buffer, mark);
10889 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10890 G_CALLBACK(text_inserted),
10892 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10894 if (prefs_common.autosave &&
10895 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10896 compose->draft_timeout_tag != -2 /* disabled while loading */)
10897 compose->draft_timeout_tag = g_timeout_add
10898 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10900 static gint compose_defer_auto_save_draft(Compose *compose)
10902 compose->draft_timeout_tag = -1;
10903 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10908 static void compose_check_all(GtkAction *action, gpointer data)
10910 Compose *compose = (Compose *)data;
10911 if (!compose->gtkaspell)
10914 if (gtkut_widget_has_focus(compose->subject_entry))
10915 claws_spell_entry_check_all(
10916 CLAWS_SPELL_ENTRY(compose->subject_entry));
10918 gtkaspell_check_all(compose->gtkaspell);
10921 static void compose_highlight_all(GtkAction *action, gpointer data)
10923 Compose *compose = (Compose *)data;
10924 if (compose->gtkaspell) {
10925 claws_spell_entry_recheck_all(
10926 CLAWS_SPELL_ENTRY(compose->subject_entry));
10927 gtkaspell_highlight_all(compose->gtkaspell);
10931 static void compose_check_backwards(GtkAction *action, gpointer data)
10933 Compose *compose = (Compose *)data;
10934 if (!compose->gtkaspell) {
10935 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10939 if (gtkut_widget_has_focus(compose->subject_entry))
10940 claws_spell_entry_check_backwards(
10941 CLAWS_SPELL_ENTRY(compose->subject_entry));
10943 gtkaspell_check_backwards(compose->gtkaspell);
10946 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10948 Compose *compose = (Compose *)data;
10949 if (!compose->gtkaspell) {
10950 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10954 if (gtkut_widget_has_focus(compose->subject_entry))
10955 claws_spell_entry_check_forwards_go(
10956 CLAWS_SPELL_ENTRY(compose->subject_entry));
10958 gtkaspell_check_forwards_go(compose->gtkaspell);
10963 *\brief Guess originating forward account from MsgInfo and several
10964 * "common preference" settings. Return NULL if no guess.
10966 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10968 PrefsAccount *account = NULL;
10970 cm_return_val_if_fail(msginfo, NULL);
10971 cm_return_val_if_fail(msginfo->folder, NULL);
10972 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10974 if (msginfo->folder->prefs->enable_default_account)
10975 account = account_find_from_id(msginfo->folder->prefs->default_account);
10978 account = msginfo->folder->folder->account;
10980 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10982 Xstrdup_a(to, msginfo->to, return NULL);
10983 extract_address(to);
10984 account = account_find_from_address(to, FALSE);
10987 if (!account && prefs_common.forward_account_autosel) {
10988 gchar cc[BUFFSIZE];
10989 if (!procheader_get_header_from_msginfo
10990 (msginfo, cc,sizeof cc , "Cc:")) {
10991 gchar *buf = cc + strlen("Cc:");
10992 extract_address(buf);
10993 account = account_find_from_address(buf, FALSE);
10997 if (!account && prefs_common.forward_account_autosel) {
10998 gchar deliveredto[BUFFSIZE];
10999 if (!procheader_get_header_from_msginfo
11000 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11001 gchar *buf = deliveredto + strlen("Delivered-To:");
11002 extract_address(buf);
11003 account = account_find_from_address(buf, FALSE);
11010 gboolean compose_close(Compose *compose)
11014 if (!g_mutex_trylock(compose->mutex)) {
11015 /* we have to wait for the (possibly deferred by auto-save)
11016 * drafting to be done, before destroying the compose under
11018 debug_print("waiting for drafting to finish...\n");
11019 compose_allow_user_actions(compose, FALSE);
11020 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11023 cm_return_val_if_fail(compose, FALSE);
11024 gtkut_widget_get_uposition(compose->window, &x, &y);
11025 if (!compose->batch) {
11026 prefs_common.compose_x = x;
11027 prefs_common.compose_y = y;
11029 g_mutex_unlock(compose->mutex);
11030 compose_destroy(compose);
11035 * Add entry field for each address in list.
11036 * \param compose E-Mail composition object.
11037 * \param listAddress List of (formatted) E-Mail addresses.
11039 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11042 node = listAddress;
11044 addr = ( gchar * ) node->data;
11045 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11046 node = g_list_next( node );
11050 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11051 guint action, gboolean opening_multiple)
11053 gchar *body = NULL;
11054 GSList *new_msglist = NULL;
11055 MsgInfo *tmp_msginfo = NULL;
11056 gboolean originally_enc = FALSE;
11057 gboolean originally_sig = FALSE;
11058 Compose *compose = NULL;
11059 gchar *s_system = NULL;
11061 cm_return_if_fail(msgview != NULL);
11063 cm_return_if_fail(msginfo_list != NULL);
11065 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11066 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11067 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11069 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11070 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11071 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11072 orig_msginfo, mimeinfo);
11073 if (tmp_msginfo != NULL) {
11074 new_msglist = g_slist_append(NULL, tmp_msginfo);
11076 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11077 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11078 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11080 tmp_msginfo->folder = orig_msginfo->folder;
11081 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11082 if (orig_msginfo->tags) {
11083 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11084 tmp_msginfo->folder->tags_dirty = TRUE;
11090 if (!opening_multiple)
11091 body = messageview_get_selection(msgview);
11094 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11095 procmsg_msginfo_free(tmp_msginfo);
11096 g_slist_free(new_msglist);
11098 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11100 if (compose && originally_enc) {
11101 compose_force_encryption(compose, compose->account, FALSE, s_system);
11104 if (compose && originally_sig && compose->account->default_sign_reply) {
11105 compose_force_signing(compose, compose->account, s_system);
11109 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11112 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11115 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11116 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11117 GSList *cur = msginfo_list;
11118 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11119 "messages. Opening the windows "
11120 "could take some time. Do you "
11121 "want to continue?"),
11122 g_slist_length(msginfo_list));
11123 if (g_slist_length(msginfo_list) > 9
11124 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11125 != G_ALERTALTERNATE) {
11130 /* We'll open multiple compose windows */
11131 /* let the WM place the next windows */
11132 compose_force_window_origin = FALSE;
11133 for (; cur; cur = cur->next) {
11135 tmplist.data = cur->data;
11136 tmplist.next = NULL;
11137 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11139 compose_force_window_origin = TRUE;
11141 /* forwarding multiple mails as attachments is done via a
11142 * single compose window */
11143 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11147 void compose_check_for_email_account(Compose *compose)
11149 PrefsAccount *ac = NULL, *curr = NULL;
11155 if (compose->account && compose->account->protocol == A_NNTP) {
11156 ac = account_get_cur_account();
11157 if (ac->protocol == A_NNTP) {
11158 list = account_get_list();
11160 for( ; list != NULL ; list = g_list_next(list)) {
11161 curr = (PrefsAccount *) list->data;
11162 if (curr->protocol != A_NNTP) {
11168 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11173 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11174 const gchar *address)
11176 GSList *msginfo_list = NULL;
11177 gchar *body = messageview_get_selection(msgview);
11180 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11182 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11183 compose_check_for_email_account(compose);
11184 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11185 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11186 compose_reply_set_subject(compose, msginfo);
11189 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11192 void compose_set_position(Compose *compose, gint pos)
11194 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11196 gtkut_text_view_set_position(text, pos);
11199 gboolean compose_search_string(Compose *compose,
11200 const gchar *str, gboolean case_sens)
11202 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11204 return gtkut_text_view_search_string(text, str, case_sens);
11207 gboolean compose_search_string_backward(Compose *compose,
11208 const gchar *str, gboolean case_sens)
11210 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11212 return gtkut_text_view_search_string_backward(text, str, case_sens);
11215 /* allocate a msginfo structure and populate its data from a compose data structure */
11216 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11218 MsgInfo *newmsginfo;
11220 gchar buf[BUFFSIZE];
11222 cm_return_val_if_fail( compose != NULL, NULL );
11224 newmsginfo = procmsg_msginfo_new();
11227 get_rfc822_date(buf, sizeof(buf));
11228 newmsginfo->date = g_strdup(buf);
11231 if (compose->from_name) {
11232 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11233 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11237 if (compose->subject_entry)
11238 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11240 /* to, cc, reply-to, newsgroups */
11241 for (list = compose->header_list; list; list = list->next) {
11242 gchar *header = gtk_editable_get_chars(
11244 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11245 gchar *entry = gtk_editable_get_chars(
11246 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11248 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11249 if ( newmsginfo->to == NULL ) {
11250 newmsginfo->to = g_strdup(entry);
11251 } else if (entry && *entry) {
11252 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11253 g_free(newmsginfo->to);
11254 newmsginfo->to = tmp;
11257 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11258 if ( newmsginfo->cc == NULL ) {
11259 newmsginfo->cc = g_strdup(entry);
11260 } else if (entry && *entry) {
11261 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11262 g_free(newmsginfo->cc);
11263 newmsginfo->cc = tmp;
11266 if ( strcasecmp(header,
11267 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11268 if ( newmsginfo->newsgroups == NULL ) {
11269 newmsginfo->newsgroups = g_strdup(entry);
11270 } else if (entry && *entry) {
11271 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11272 g_free(newmsginfo->newsgroups);
11273 newmsginfo->newsgroups = tmp;
11281 /* other data is unset */
11287 /* update compose's dictionaries from folder dict settings */
11288 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11289 FolderItem *folder_item)
11291 cm_return_if_fail(compose != NULL);
11293 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11294 FolderItemPrefs *prefs = folder_item->prefs;
11296 if (prefs->enable_default_dictionary)
11297 gtkaspell_change_dict(compose->gtkaspell,
11298 prefs->default_dictionary, FALSE);
11299 if (folder_item->prefs->enable_default_alt_dictionary)
11300 gtkaspell_change_alt_dict(compose->gtkaspell,
11301 prefs->default_alt_dictionary);
11302 if (prefs->enable_default_dictionary
11303 || prefs->enable_default_alt_dictionary)
11304 compose_spell_menu_changed(compose);